Commit fa42ef17428032c1f5c18a6f0badc1a680220fde

Authored by Василь Свідрик
1 parent 834b86d05b
Exists in master

add k2site compatibility

Showing 43 changed files with 437 additions and 724 deletions Side-by-side Diff

__pycache__/main.cpython-310.pyc
No preview for this file type
__pycache__/routes.cpython-310.pyc
No preview for this file type
components/__pycache__/__init__.cpython-310.pyc
No preview for this file type
components/k2adm/.gitignore
1   - idea
2   -__pycache__/
3   -*/__pycache__/
4   -*.py[cod]
5   -.pyc
components/k2adm/k2adm/languages/en/LC_MESSAGES/messages.mo
No preview for this file type
components/k2adm/k2adm/languages/en/LC_MESSAGES/messages.po
1   -msgid "title_menu_items"
2   -msgstr "Site administration"
3   -
4   -msgid "menu_items_child_1"
5   -msgstr "Password change"
6   -
7   -msgid "menu_items_child_2"
8   -msgstr "New user"
9   -
10   -msgid "menu_items_child_3"
11   -msgstr "Users"
12   -
13   -
14   -msgid "menu_items_child_4"
15   -msgstr "Users are clients"
16   -
17   -
18   -msgid "menu_items_child_5"
19   -msgstr "Roles"
20   -
21   -
22   -msgid "menu_items_child_6"
23   -msgstr "Access to the menu"
components/k2adm/k2adm/languages/uk/LC_MESSAGES/messages.mo
No preview for this file type
components/k2adm/k2adm/languages/uk/LC_MESSAGES/messages.po
1   -msgid "title_menu_items"
2   -msgstr "Адміністрування сайту"
3   -
4   -
5   -msgid "menu_items_child_1"
6   -msgstr "Зміна паролю"
7   -
8   -msgid "menu_items_child_2"
9   -msgstr "Новий користувач"
10   -
11   -msgid "menu_items_child_3"
12   -msgstr "Користувачі"
13   -
14   -
15   -msgid "menu_items_child_4"
16   -msgstr "Користувачі кліенти"
17   -
18   -
19   -msgid "menu_items_child_5"
20   -msgstr "Ролі"
21   -
22   -
23   -msgid "menu_items_child_6"
24   -msgstr "Доступ до меню"
components/k2adm/k2adm/models.py
1   -import uuid
2   -from sqlalchemy import Column, Integer, String
3   -from sqlalchemy.ext.declarative import declarative_base
4   -from flask_jwt_extended import create_access_token
5   -from datetime import timedelta
6   -import hashlib
7   -from flask import current_app as k2
8   -
9   -Base = declarative_base()
10   -
11   -
12   -class k2users(Base):
13   - __tablename__ = 'k2users'
14   - user_id = Column(Integer, primary_key=True)
15   - login = Column(String(70), nullable=False)
16   - name = Column(String(50), nullable=False)
17   - email = Column(String(150), nullable=False)
18   - password = Column(String(150), nullable=False)
19   - phone = Column(String(20))
20   - roleid = Column(String(150), nullable=False)
21   -
22   - def __init__(self, **kwargs):
23   - self.user_id = hashlib.md5(str(uuid.uuid4()).encode()).hexdigest()
24   - self.login = kwargs.get('login')
25   - self.password = hashlib.md5(kwargs.get('password').encode()).hexdigest()
26   - self.name = kwargs.get('name')
27   - self.email = kwargs.get('email')
28   - self.phone = kwargs.get('phone')
29   - self.roleid = kwargs.get('roleid')
30   -
31   - def get_token(self, expire_time=24):
32   - expires_delta = timedelta(expire_time)
33   - token = create_access_token(identity=self.user_id, expires_delta=expires_delta)
34   -
35   - db = k2.extensions['sqlalchemy'].db
36   - connection = db.engine.connect()
37   - result = connection.execute(
38   - db.text(f"SELECT rolename FROM k2roles where roleid = '{self.roleid}'")).fetchone()
39   - rolename = result[0] if result else None
40   -
41   - userData = {
42   - "avatar": "/src/assets/images/avatars/avatar-4.png",
43   - "email": self.email,
44   - "fullName": self.name,
45   - "id": self.user_id,
46   - "role": rolename,
47   - "username": self.login
48   - }
49   -
50   - userAbilities = []
51   - query = f"""
52   - SELECT menus.namemenu, prava.r, prava.w, prava.i, prava.d, prava.c,
53   - prava.exp, prava.imp, prava.settable, prava.cutpast, prava.enable, prava.del
54   - FROM k2admin_menus menus
55   - JOIN k2admin_menus_prava prava ON menus.menuid = prava.menuid
56   - WHERE prava.roleid = {self.roleid}
57   - """
58   - menus = connection.execute(db.text(query)).fetchall()
59   -
60   - for menu in menus:
61   - abilities_dict = {
62   - "subject": menu[0],
63   - "read": menu[1],
64   - "write": menu[2],
65   - "insert": menu[3],
66   - "delete": menu[4],
67   - "copy": menu[5],
68   - "export": menu[6],
69   - "import": menu[7],
70   - "settable": menu[8],
71   - "cutpast": menu[9],
72   - "enable": menu[10],
73   - "del": menu[11],
74   - }
75   -
76   - abilities_dict_copy = abilities_dict.copy()
77   - for key, value in abilities_dict_copy.items():
78   - if value == 0:
79   - del abilities_dict[key]
80   - userAbilities.append(abilities_dict)
81   -
82   - return (token, userData, userAbilities)
83   -
84   - @classmethod
85   - def authenticate(cls, login, password):
86   - session = k2.extensions['sqlalchemy'].db.session
87   - user = ''
88   - try:
89   - user = session.query(cls).filter(cls.login == login).filter(cls.password == hashlib.md5(password.encode()).hexdigest()).one()
90   - except:
91   - user = None
92   - return user
93   -
94   - @classmethod
95   - def user_by_login(cls, login):
96   - session = k2.extensions['sqlalchemy'].db.session
97   - user = session.query(cls).filter(cls.login == login).first()
98   - return user
components/k2adm/k2adm/views.py
1   -import hashlib
2   -import uuid
3   -from flask import Blueprint, request, jsonify
4   -from flask import current_app as k2
5   -from flask_jwt_extended import jwt_required, get_jwt_identity
6   -from flask_babel import gettext
7   -
8   -from .models import k2users
9   -
10   -
11   -k2adm = Blueprint('k2adm', __name__)
12   -
13   -
14   -@k2adm.route('/usersclients', methods=["GET"])
15   -@jwt_required()
16   -def users_clients():
17   - return jsonify({"data": "user roles"})
18   -
19   -
20   -@k2adm.route('/users', methods=["GET"])
21   -@jwt_required()
22   -def grid_users():
23   - return jsonify({"data": "users"})
24   -
25   -
26   -@k2adm.route('/roles', methods=["GET"])
27   -@jwt_required()
28   -def grid_roles():
29   - return jsonify({"data": "roles"})
30   -
31   -
32   -
33   -@k2adm.route('/api_get_roles', methods=["GET"])
34   -@jwt_required()
35   -def roles():
36   - db = k2.extensions['sqlalchemy'].db
37   - connection = db.engine.connect()
38   - query = db.text("SELECT roleid, rolename FROM k2roles")
39   - result = connection.execute(query).fetchall()
40   - roles = [dict(id=res.roleid, role=res.rolename) for res in result]
41   - return {'data': roles}
42   -
43   -
44   -@k2adm.route('/newUser', methods=["GET", "POST"])
45   -@jwt_required()
46   -def newUser():
47   - session = k2.extensions['sqlalchemy'].db.session
48   - exist = k2users.user_by_login(request.json['login'])
49   - if not exist:
50   - res = k2users(
51   - user_id=hashlib.md5(str(uuid.uuid4()).encode()).hexdigest(),
52   - login=request.json['login'],
53   - name=request.json['name'],
54   - email=request.json['email'],
55   - password=request.json['password'],
56   - phone=request.json['phone'],
57   - roleid=request.json['role']
58   - )
59   -
60   - if res:
61   - session.add(res)
62   - session.commit()
63   - return jsonify({'status': '200', 'message': 'New user created'})
64   - else:
65   - return jsonify({'status': '409', 'message': 'Such user exist'}), 409
66   -
67   -
68   -
69   -@k2adm.route('/changePassword', methods=["POST"])
70   -@jwt_required()
71   -def changePassword():
72   - session = k2.extensions['sqlalchemy'].db.session
73   - current_user = get_jwt_identity()
74   -
75   - user = session.query(k2users).filter_by(user_id=current_user['user_id']).first()
76   - if user.password == hashlib.md5(request.json['old_password'].encode()).hexdigest():
77   - if request.json['new_password'] == request.json['re_new_password']:
78   - user.password = hashlib.md5(request.json['new_password'].encode()).hexdigest()
79   - session.commit()
80   - else:
81   - return jsonify({'status': '400', 'message': 'Пароли не совпвдает'}), 400
82   - else:
83   - jsonify({'status': '404', 'message': "Пароль не найден в базе"}), 404
84   - return jsonify({'status': 'ok'})
85   -
86   -
87   -@k2adm.route('/menu-admin-items')
88   -def menu_items():
89   - data = [
90   - {
91   - # "title": 'Site administration',
92   - "title": f"{gettext('title_menu_items')}",
93   - "icon": {"icon": 'mdi-account-circle-outline'},
94   - "children": [
95   - {'title': f"{gettext('menu_items_child_1')}", 'to': 'adm-changePassword'},
96   - {'title': f"{gettext('menu_items_child_2')}", 'to': 'adm-newUser'},
97   - {'title': f"{gettext('menu_items_child_3')}", 'to': 'adm-users'},
98   - {'title': f"{gettext('menu_items_child_4')}", 'to': 'adm-usersclients'},
99   - {'title': f"{gettext('menu_items_child_5')}", 'to': 'adm-roles'},
100   - {'title': f"{gettext('menu_items_child_6')}", 'to': 'adm-menuaaccess'},
101   - ],
102   - }]
103   - return data
components/k2adm/requirements.txt
1   -alembic==1.11.1
2   -Babel==2.12.1
3   -bcrypt==4.0.1
4   -bidict==0.22.1
5   -blinker==1.6.2
6   -certifi==2023.5.7
7   -charset-normalizer==3.1.0
8   -click==8.1.3
9   -colorama==0.4.6
10   -dnspython==2.3.0
11   -email-validator==2.0.0.post2
12   -Flask==2.3.2
13   -flask-babel==3.1.0
14   -Flask-Bcrypt==1.0.1
15   -Flask-Cors==3.0.10
16   -Flask-JWT-Extended==4.5.2
17   -Flask-Login==0.6.2
18   -Flask-Migrate==4.0.4
19   -Flask-MySQLdb==1.0.1
20   -Flask-SQLAlchemy==3.0.3
21   -flask-staticdirs==1.0.1
22   -Flask-WTF==1.1.1
23   -greenlet==2.0.2
24   -idna==3.4
25   -itsdangerous==2.1.2
26   -Jinja2==3.1.2
27   -jsonify==0.5
28   -Mako==1.2.4
29   -MarkupSafe==2.1.2
30   -mysql==0.0.3
31   -mysql-connector==2.2.9
32   -mysql-connector-python==8.0.33
33   -mysqlclient==2.1.1
34   -peppercorn==0.6
35   -protobuf==3.20.3
36   -PyJWT==2.7.0
37   -python-engineio==4.4.1
38   -python-socketio==5.8.0
39   -pytz==2023.3
40   -PyYAML==6.0
41   -requests==2.31.0
42   -six==1.16.0
43   -SQLAlchemy==2.0.15
44   -typing_extensions==4.6.2
45   -urllib3==2.0.2
46   -Werkzeug==2.3.4
47   -WTForms==3.0.1
components/k2adm/setup.py
1   -from setuptools import setup
2   -
3   -setup(
4   - name='k2adm',
5   - version='1.0.1',
6   - description='Description of my project',
7   - author='k2 cloud',
8   - author_email='ys@corp2.eu',
9   - keywords="adm, k2",
10   - python_requires=">=3.7, <=3.11.2",
11   - packages=['adm'],
12   - package_data={ # Optional
13   - },
14   - install_requires=[""],
15   -)
components/k2auth/.gitignore
1   - idea
2   -__pycache__/
3   -*/__pycache__/
4   -*.py[cod]
5   -.pyc
6   -.idea/
7   -venv
components/k2auth/README.md
1   -# main.py
2   -
3   -from components.adm.adm.views import adm
4   -from components.test.test.views import test
5   -
6   -app.register_blueprint(adm, url_prefix='/adm')
7   -
8   -cd components/adm
9   -pip install -r requirements.txt
10   -
11   -
12   -endpoint "http://127.0.0.1:5000/adm/register"
13   -dictionary to register in such way
14   -{
15   - "login": "xxx",
16   - "password": "xxx",
17   - "name": "xxx",
18   - "email": "xxx"
19   -}
20   -
21   -
22   -endpoint "http://127.0.0.1:5000/adm/login"
23   -dictionary to login in such way
24   -{
25   - "login":"xxx",
26   - "password":"xxx"
27   -}
28   -
29   -endpoint http://127.0.0.1:5000/adm/changePassword
30   -dictionary to changePassword in such way
31   -{
32   - "old_password":"xxx",
33   - "new_password": "xxx",
34   - "re_new_password": "xxx"
35   -}
36   -endpoint http://127.0.0.1:5000/adm/newUser
37   -{
38   - "login": "xxx",
39   - "password": "xxx",
40   - "name": "xxx",
41   - "email": "xxx",
42   - "phone": "xxx",
43   - "repassword": "xxx"
44   -
45   -}
components/k2auth/k2auth/models.py
1   -
2   -import uuid
3   -from sqlalchemy import Column, Integer, String
4   -from sqlalchemy.ext.declarative import declarative_base
5   -from flask_jwt_extended import create_access_token
6   -from datetime import timedelta
7   -import hashlib
8   -from flask import current_app as k2
9   -
10   -Base = declarative_base()
11   -
12   -
13   -class k2users(Base):
14   - __tablename__ = 'k2users'
15   - user_id = Column(Integer, primary_key=True)
16   - login = Column(String(70), nullable=False)
17   - name = Column(String(50), nullable=False)
18   - email = Column(String(150), nullable=False)
19   - password = Column(String(150), nullable=False)
20   - phone = Column(String(20))
21   - roleid = Column(String(150), nullable=False)
22   -
23   - def __init__(self, **kwargs):
24   - self.user_id = hashlib.md5(str(uuid.uuid4()).encode()).hexdigest()
25   - self.login = kwargs.get('login')
26   - self.password = hashlib.md5(kwargs.get('password').encode()).hexdigest()
27   - self.name = kwargs.get('name')
28   - self.email = kwargs.get('email')
29   - self.phone = kwargs.get('phone')
30   - self.roleid = kwargs.get('roleid')
31   -
32   - def get_token(self, expire_time=24):
33   - expires_delta = timedelta(expire_time)
34   - # token = create_access_token(identity=self.user_id, expires_delta=expires_delta)
35   - token = create_access_token(identity={
36   - 'user_id': self.user_id,
37   - 'user_role': self.roleid
38   - }, expires_delta=expires_delta)
39   -
40   - db = k2.extensions['sqlalchemy'].db
41   - connection = db.engine.connect()
42   - result = connection.execute(
43   - db.text(f"SELECT rolename FROM k2roles where roleid = '{self.roleid}'")).fetchone()
44   -
45   - rolename = result[0] if result else None
46   -
47   - userData = {
48   - "avatar": "/src/assets/images/avatars/avatar-4.png",
49   - "email": self.email,
50   - "fullName": self.name,
51   - "id": self.user_id,
52   - "role": rolename,
53   - "username": self.login
54   - }
55   -
56   - userAbilities = []
57   - from k2.k2comp import Component
58   - components = Component.query.filter(Component.installed == 1).all()
59   - components_names = [component.name for component in components]
60   -
61   -
62   -
63   - abilities_query = f"""
64   - SELECT menus.namemenu,
65   - prava.r, prava.w, prava.i, prava.d, prava.c, prava.exp, prava.imp, prava.settable, prava.cutpast,
66   - prava.enable, prava.del
67   - FROM k2admin_menus menus
68   - JOIN k2admin_menus_prava prava ON menus.menuid = prava.menuid
69   - WHERE prava.roleid = {self.roleid} AND menus.namemenu IN {tuple(components_names)}
70   - """
71   - abilities_list = connection.execute(db.text(abilities_query)).fetchall()
72   -
73   - for ability in abilities_list:
74   - abilities_dict = {
75   - "subject": ability[0],
76   - "read": ability[1],
77   - "write": ability[2],
78   - "insert": ability[3],
79   - "delete": ability[4],
80   - "copy": ability[5],
81   - "export": ability[6],
82   - "import": ability[7],
83   - "settable": ability[8],
84   - "cutpast": ability[9],
85   - "enable": ability[10],
86   - "del": ability[11]
87   - }
88   -
89   - abilities_dict_copy = abilities_dict.copy()
90   - for key, value in abilities_dict_copy.items():
91   - if value == 0:
92   - del abilities_dict[key]
93   - userAbilities.append(abilities_dict)
94   -
95   - return (token, userData, userAbilities)
96   -
97   -
98   - @classmethod
99   - def authenticate(cls, login, password):
100   - session = k2.extensions['sqlalchemy'].db.session
101   - user = ''
102   - try:
103   - user = session.query(cls).filter(cls.login == login).filter(cls.password == hashlib.md5(password.encode()).hexdigest()).one()
104   - except:
105   - user = None
106   - return user
107   -
108   - @classmethod
109   - def user_by_login(cls, login):
110   - session = k2.extensions['sqlalchemy'].db.session
111   - user = session.query(cls).filter(cls.login == login).first()
112   - return user
components/k2auth/k2auth/views.py
1   -# import hashlib
2   -# import uuid
3   -from flask import Blueprint, request, jsonify
4   -from flask import current_app as k2
5   -from flask_jwt_extended import jwt_required
6   -import re
7   -from .models import k2users
8   -
9   -k2auth = Blueprint('k2auth', __name__, template_folder='templates', static_folder='static', static_url_path='/vue2')
10   -
11   -
12   -class Auth():
13   - def __init__(self):
14   - pass
15   -
16   - def register(self):
17   - params = request.json
18   - session = k2.extensions['sqlalchemy'].db.session
19   - password = params.get('password')
20   - login = params.get('login')
21   - user_exist = k2users.user_by_login(login)
22   - if user_exist:
23   - return jsonify({'status': '409', 'message': 'Such user exist'}), 409
24   -
25   - if len(password) < 6:
26   - return jsonify({'status': '400', 'message': 'password has not 6 simbols'}), 400
27   - if re.search(r'[a-zA-Z]', password) is None or re.search(r'\d', password) is None:
28   - return jsonify({'status': '400', 'message': 'Password must contain both letters and numbers'}), 400
29   -
30   - try:
31   - user = k2users(**params)
32   - session.add(user)
33   - session.commit()
34   - token, userData, userAbilities = user.get_token()
35   - print(userAbilities)
36   -
37   - return {'accessToken': token, "userData": userData, "userAbilities": userAbilities}
38   - except:
39   - return jsonify({'status': '400', 'message': "Missing required fields"}), 400
40   -
41   -
42   -
43   - def login(self):
44   - params = request.json
45   - user = k2users.authenticate(**params)
46   - if user:
47   - token, userData, userAbilities = user.get_token()
48   - return {'accessToken': token, "userData": userData, "userAbilities": userAbilities}
49   - else:
50   - # k2.logger.error(('%s failed to log in ', params.get('login'), params.get('password')))
51   - return jsonify({'status': '401', 'message': 'Unauthorized'}), 401
52   -
53   -
54   - @jwt_required()
55   - def logout(self):
56   - response = {
57   - 'message': 'Logged out successfully'
58   - }
59   - return jsonify(response)
60   -
61   -
62   -
63   -auth = Auth()
64   -k2auth.add_url_rule('/login', methods=['POST'], view_func=auth.login)
65   -k2auth.add_url_rule('/logout', methods=['GET'], view_func=auth.logout)
66   -k2auth.add_url_rule('/register', methods=['POST'], view_func=auth.register)
components/k2auth/requirements.txt
1   -alembic==1.11.1
2   -Babel==2.12.1
3   -bcrypt==4.0.1
4   -bidict==0.22.1
5   -blinker==1.6.2
6   -certifi==2023.5.7
7   -charset-normalizer==3.1.0
8   -click==8.1.3
9   -colorama==0.4.6
10   -dnspython==2.3.0
11   -email-validator==2.0.0.post2
12   -Flask==2.3.2
13   -flask-babel==3.1.0
14   -Flask-Bcrypt==1.0.1
15   -Flask-Cors==3.0.10
16   -Flask-JWT-Extended==4.5.2
17   -Flask-Login==0.6.2
18   -Flask-Migrate==4.0.4
19   -Flask-MySQLdb==1.0.1
20   -Flask-SQLAlchemy==3.0.3
21   -flask-staticdirs==1.0.1
22   -Flask-WTF==1.1.1
23   -greenlet==2.0.2
24   -idna==3.4
25   -itsdangerous==2.1.2
26   -Jinja2==3.1.2
27   -jsonify==0.5
28   -Mako==1.2.4
29   -MarkupSafe==2.1.2
30   -mysql==0.0.3
31   -mysql-connector==2.2.9
32   -mysql-connector-python==8.0.33
33   -mysqlclient==2.1.1
34   -peppercorn==0.6
35   -protobuf==3.20.3
36   -PyJWT==2.7.0
37   -python-engineio==4.4.1
38   -python-socketio==5.8.0
39   -pytz==2023.3
40   -PyYAML==6.0
41   -requests==2.31.0
42   -six==1.16.0
43   -SQLAlchemy==2.0.15
44   -typing_extensions==4.6.2
45   -urllib3==2.0.2
46   -Werkzeug==2.3.4
47   -WTForms==3.0.1
components/k2auth/setup.py
1   -from setuptools import setup
2   -
3   -setup(
4   - name='k2auth',
5   - version='1.0.1',
6   - description='Description of my project',
7   - author='k2 cloud',
8   - author_email='ys@corp2.eu',
9   - keywords="k2auth, k2",
10   - python_requires=">=3.7, <=3.11.2",
11   - packages=['k2auth'],
12   - package_data={ # Optional
13   - },
14   - install_requires=[""],
15   -)
components/k2test/__pycache__/__init__.cpython-310.pyc
No preview for this file type
components/k2test/k2test/__pycache__/__init__.cpython-310.pyc
No preview for this file type
components/k2test/k2test/__pycache__/views.cpython-310.pyc
No preview for this file type
No preview for this file type
k2/__pycache__/__init__.cpython-310.pyc
No preview for this file type
k2/__pycache__/k2admmenu.cpython-310.pyc
No preview for this file type
k2/__pycache__/k2cfg.cpython-310.pyc
No preview for this file type
k2/__pycache__/k2comp.cpython-310.pyc
No preview for this file type
k2/__pycache__/k2obj.cpython-310.pyc
No preview for this file type
k2/__pycache__/k2rout.cpython-310.pyc
No preview for this file type
... ... @@ -15,7 +15,7 @@
15 15 version = '2.5.1.27'
16 16 db = SQLAlchemy()
17 17 domain = 'http://127.0.0.1:5000/'
18   - update_domain ='http://127.0.0.1:8002/'
  18 + update_domain ='http://185.174.172.137/'
19 19 venv_bin_path = os.path.join(os.path.dirname(sys.prefix), 'venv/Scripts')
20 20 json_sort_keys = False
21 21 timezone = pytz.timezone('Europe/Kiev')
... ... @@ -34,6 +34,7 @@
34 34 def load_babel_translation_directories(cls):
35 35 with open('languages/babel_translation_directories.yml', 'r') as f:
36 36 data = yaml.safe_load(f)
  37 + print(data['babel_translation_directories'])
37 38 cls.babel_translation_directories = data['babel_translation_directories']
38 39  
39 40 @classmethod
... ... @@ -78,12 +78,17 @@
78 78 return result
79 79  
80 80  
81   -@components_bp.route('/home', methods=['GET', 'POST'])
82   -@first_login_required
  81 +@components_bp.route('/', methods=['GET', 'POST'])
83 82 def home():
84   - return redirect('/dashboard')
  83 + components = Component.query.all()
  84 + components_names = [component.name for component in components]
  85 + if 'k2site' in components_names:
  86 + return redirect(url_for('k2test.new_component2')) #змінити на ендпоінт головної k2site
  87 + else:
  88 + return redirect('/dashboard')
85 89  
86 90  
  91 +
87 92 @components_bp.route('/first-login', methods=['GET', 'POST'])
88 93 def first_login():
89 94 if request.method == "POST":
90 95  
91 96  
92 97  
93 98  
94 99  
95 100  
96 101  
... ... @@ -101,43 +106,47 @@
101 106 def change_language(lang):
102 107 session['lang'] = lang
103 108 k2.current_language = lang
104   - return redirect(url_for('components.dashboard'))
  109 + return redirect(request.referrer)
105 110  
106 111  
107 112 @components_bp.route('/dashboard')
108   -@first_login_required
109 113 def dashboard():
110   - # Компоненти доступні для встановлення
111   - # GET-запит до API
112   - try:
113   - response = requests.get(f'{k2.update_domain}api/components')
114   - json_data = response.json()
115   - # Перетворення JSON-об'єкту на масив
116   - component_server = [item for item in json_data]
117   - except:
118   - component_server = None
119   -
120   - # Встановлені компоненти
121 114 components = Component.query.all()
122 115 components_names = [component.name for component in components]
123   - # Отримання значення пошукового запиту з параметрів URL
124   - search_query = request.args.get('search_query')
125   - # print(search_query)
126   - filtered_components = []
127   - if search_query:
128   - filtered_components = [component for component in component_server if
129   - (search_query.lower() in component['name'].lower() if component['name'] else False) or
130   - (search_query.lower() in component['description'].lower() if component[
131   - 'description'] else False)]
  116 + if 'k2site' in components_names:
  117 + return redirect(url_for('components_bp.component_list_dasb')) # змінити на ендпоінт головної k2site
132 118 else:
133   - filtered_components = component_server
134   - current_language = k2.current_language
  119 + # Компоненти доступні для встановлення
  120 + # GET-запит до API
  121 + try:
  122 + response = requests.get(f'{k2.update_domain}api/components')
  123 + json_data = response.json()
  124 + # Перетворення JSON-об'єкту на масив
  125 + component_server = [item for item in json_data]
  126 + except:
  127 + component_server = None
135 128  
136   - return render_template('dashboard.html', components=components,
137   - components_names=components_names, component_server=filtered_components,
138   - search_query=search_query, language=k2.menu)
  129 + # Встановлені компоненти
  130 + components = Component.query.all()
  131 + components_names = [component.name for component in components]
  132 + # Отримання значення пошукового запиту з параметрів URL
  133 + search_query = request.args.get('search_query')
  134 + # print(search_query)
  135 + filtered_components = []
  136 + if search_query:
  137 + filtered_components = [component for component in component_server if
  138 + (search_query.lower() in component['name'].lower() if component['name'] else False) or
  139 + (search_query.lower() in component['description'].lower() if component[
  140 + 'description'] else False)]
  141 + else:
  142 + filtered_components = component_server
  143 + current_language = k2.current_language
139 144  
  145 + return render_template('dashboard.html', components=components,
  146 + components_names=components_names, component_server=filtered_components,
  147 + search_query=search_query, language=current_language)
140 148  
  149 +
141 150 @components_bp.route('/show_components/<string:component_id>')
142 151 def show_components(component_id):
143 152 response = requests.get(f'{k2.update_domain}api/components')
... ... @@ -287,7 +296,6 @@
287 296 db.session.commit()
288 297  
289 298 # add language folders
290   -
291 299 k2.search_babel_translation_directories()
292 300 return f'''Component installed successfully: {selected_component["name"]} v{version} {k2.babel_translation_directories},
293 301 \n \n please wait installing requirments...
294 302  
295 303  
... ... @@ -404,39 +412,39 @@
404 412 return 'Error removing component: ' + str(e)
405 413  
406 414  
407   -@components_bp.route('/component/add')
408   -def component_add():
409   - # components for install
410   - try:
411   - # GET-requests to API
412   - response = requests.get(f'{k2.update_domain}api/components')
413   - json_data = response.json()
414   - for item in json_data:
415   - item['button'] = f"{k2.domain}/install_components/{item['id']}"
  415 +# @components_bp.route('/component/add')
  416 +# def component_add():
  417 +# # components for install
  418 +# try:
  419 +# # GET-requests to API
  420 +# response = requests.get(f'{k2.update_domain}api/components')
  421 +# json_data = response.json()
  422 +# for item in json_data:
  423 +# item['button'] = f"{k2.domain}/install_components/{item['id']}"
  424 +#
  425 +# except:
  426 +# json_data = None
  427 +# return jsonify(json_data)
  428 +#
  429 +#
  430 +# @components_bp.route('/component/list')
  431 +# def component_list():
  432 +# # Встановлені компоненти
  433 +# component_list = []
  434 +# components = Component.query.all()
  435 +# for component in components:
  436 +# component_dict = {}
  437 +# component_dict['name'] = component.name
  438 +# component_dict['id'] = component.id
  439 +# component_dict['description'] = component.description
  440 +# component_dict['version'] = component.version
  441 +# component_dict['button_off'] =f"{k2.domain}/remove_dependencies/{component.id}"
  442 +# component_dict['button_on'] = f"{k2.domain}/add_dependencies/{component.id}"
  443 +# component_dict['button_del'] = f"{k2.domain}/remove-component/{component.id}"
  444 +# component_list.append(component_dict)
  445 +# return jsonify(component_list)
416 446  
417   - except:
418   - json_data = None
419   - return jsonify(json_data)
420 447  
421   -
422   -@components_bp.route('/component/list')
423   -def component_list():
424   - # Встановлені компоненти
425   - component_list = []
426   - components = Component.query.all()
427   - for component in components:
428   - component_dict = {}
429   - component_dict['name'] = component.name
430   - component_dict['id'] = component.id
431   - component_dict['description'] = component.description
432   - component_dict['version'] = component.version
433   - component_dict['button_off'] =f"{k2.domain}/remove_dependencies/{component.id}"
434   - component_dict['button_on'] = f"{k2.domain}/add_dependencies/{component.id}"
435   - component_dict['button_del'] = f"{k2.domain}/remove-component/{component.id}"
436   - component_list.append(component_dict)
437   - return jsonify(component_list)
438   -
439   -
440 448 # menu
441 449 @components_bp.route('/api/add-to-menu', methods=['GET'])
442 450 def add_to_menu():
... ... @@ -445,7 +453,7 @@
445 453 data.append(response.json()[0])
446 454 prev_menu = response.json()[0]['title']
447 455 for item in response.json()[0]['children']:
448   - name_menu = item['to']
  456 + name_menu = str(item['to']).replace('/','-')
449 457 caption_menu = item['title']
450 458 add_menu_with_permissions_db(name_menu, prev_menu, caption_menu)
451 459 components = Component.query.all()
... ... @@ -505,7 +513,6 @@
505 513 caption=caption_menu,
506 514 module_name=name_menu
507 515 )
508   - # Додавання об'єкта k2admin_menus до сесії
509 516 db.session.add(new_menu_element)
510 517 db.session.commit()
511 518 # Створення об'єкта k2admin_menus_prava
512 519  
513 520  
... ... @@ -528,11 +535,42 @@
528 535 db.session.add(new_prava)
529 536 db.session.commit()
530 537  
  538 +@components_bp.route('/component-add')
  539 +def component_add_dasb():
  540 + try:
  541 + response = requests.get(f'{k2.update_domain}api/components')
  542 + json_data = response.json()
  543 + # Перетворення JSON-об'єкту на масив
  544 + component_server = [item for item in json_data]
  545 + except:
  546 + component_server = None
  547 + # Отримання значення пошукового запиту з параметрів URL
  548 + search_query = request.args.get('search_query')
  549 + # print(search_query)
  550 + filtered_components = []
  551 + if search_query:
  552 + filtered_components = [component for component in component_server if
  553 + (search_query.lower() in component['name'].lower() if component[
  554 + 'name'] else False) or
  555 + (search_query.lower() in component['description'].lower() if component[
  556 + 'description'] else False)]
  557 + else:
  558 + filtered_components = component_server
531 559  
  560 + return render_template('dashboard-add.html',
  561 + component_server=filtered_components,
  562 + )
  563 +
  564 +@components_bp.route('/component-list')
  565 +def component_list_dasb():
  566 + components = Component.query.all()
  567 + return render_template('dashboard-list.html', components=components)
  568 +
  569 +
  570 +
532 571 @components_bp.route('/menu-admin-items')
533 572 def menu_items():
534 573 menu = [
535   -
536 574 {
537 575 "title": 'Add components',
538 576 "icon": {"icon": 'mdi-account-circle-outline'},
languages/en/LC_MESSAGES/messages.mo
No preview for this file type
languages/en/LC_MESSAGES/messages.po
1   -msgid "Installed components"
  1 +msgid "t_installed_components"
2 2 msgstr "Installed components"
3 3  
4   -msgid "Name"
  4 +msgid "t_name"
5 5 msgstr "Name"
6 6  
7   -msgid "Description"
  7 +msgid "t_description"
8 8 msgstr "Description"
9 9  
10   -msgid "Version"
  10 +msgid "t_version"
11 11 msgstr "Version"
12 12  
13   -msgid "Dependencies"
  13 +msgid "t_dependencies"
14 14 msgstr "Dependencies"
15 15  
16   -msgid "Git Link"
  16 +msgid "t_git_link"
17 17 msgstr "Git Link"
18 18  
19   -msgid "Install At"
  19 +msgid "t_install_at"
20 20 msgstr "Install At"
21 21  
22   -msgid "Status"
  22 +msgid "t_status"
23 23 msgstr "Status"
24 24  
25   -msgid "Action"
  25 +msgid "t_action"
26 26 msgstr "Action"
27 27  
28   -msgid "Components Shop"
29   -msgstr "Components Shop"
  28 +msgid "t_components_shop"
  29 +msgstr "Add component"
languages/pl/LC_MESSAGES/messages.mo
No preview for this file type
languages/pl/LC_MESSAGES/messages.po
  1 +msgid ""
  2 +msgstr ""
  3 +"Content-Type: text/plain; charset=UTF-8\n"
  4 +"Content-Transfer-Encoding: 8bit\n"
  5 +
  6 +msgid "t_installed_components"
  7 +msgstr "Zainstalowane komponenty"
  8 +
  9 +msgid "t_name"
  10 +msgstr "Nazwa"
  11 +
  12 +msgid "t_description"
  13 +msgstr "Opis"
  14 +
  15 +msgid "t_version"
  16 +msgstr "Wersja"
  17 +
  18 +msgid "t_dependencies"
  19 +msgstr "Zależności"
  20 +
  21 +msgid "t_git_link"
  22 +msgstr "Link do Git"
  23 +
  24 +msgid "t_install_at"
  25 +msgstr "Zainstalowano"
  26 +
  27 +msgid "t_status"
  28 +msgstr "Status"
  29 +
  30 +msgid "t_action"
  31 +msgstr "Akcja"
  32 +
  33 +msgid "t_components_shop"
  34 +msgstr "Dodaj komponent"
languages/uk/LC_MESSAGES/messages.mo
No preview for this file type
languages/uk/LC_MESSAGES/messages.po
1   -msgid "Installed components"
2   -msgstr "Встановлені компоненти"
  1 +msgid ""
  2 +msgstr ""
  3 +"Content-Type: text/plain; charset=UTF-8\n"
  4 +"Content-Transfer-Encoding: 8bit\n"
3 5  
4   -msgid "Installed components"
  6 +msgid "t_installed_components"
5 7 msgstr "Встановлені компоненти"
6 8  
7   -msgid "Name"
  9 +msgid "t_name"
8 10 msgstr "Назва"
9 11  
10   -msgid "Description"
  12 +msgid "t_description"
11 13 msgstr "Опис"
12 14  
13   -msgid "Version"
  15 +msgid "t_version"
14 16 msgstr "Версія"
15 17  
16   -msgid "Dependencies"
  18 +msgid "t_dependencies"
17 19 msgstr "Залежності"
18 20  
19   -msgid "Git Link"
  21 +msgid "t_git_link"
20 22 msgstr "Посилання на Git"
21 23  
22   -msgid "Install At"
  24 +msgid "t_install_at"
23 25 msgstr "Встановлено"
24 26  
25   -msgid "Status"
  27 +msgid "t_status"
26 28 msgstr "Статус"
27 29  
28   -msgid "Action"
  30 +msgid "t_action"
29 31 msgstr "Дія"
30 32  
31   -msgid "Components Shop"
32   -msgstr "Магазин компонент"
  33 +msgid "t_components_shop"
  34 +msgstr "Додати компоненту"
1 1 from flask_cors import CORS
2 2 from flask_babel import Babel
3 3 from flask_jwt_extended import JWTManager
4   -#from k2.k2inst import *
  4 +#from k2.k2rout import *
5 5 from flask import Flask
6 6 from k2.k2cfg import k2
7 7  
  8 +
  9 +
8 10 # configure flask app
9 11 app = Flask(__name__)
10 12 app.config['SECRET_KEY'] = k2.secret_key
11 13 app.json.sort_keys = k2.json_sort_keys
12 14 #app.config.from_object(k2)
13 15  
  16 +
14 17 # init db
15 18 k2().init_db(app)
16 19 CORS(app)
... ... @@ -19,6 +22,13 @@
19 22 #translate
20 23 k2.load_babel_translation_directories()
21 24 app.config['BABEL_TRANSLATION_DIRECTORIES'] = k2.babel_translation_directories
  25 +app.config['BABEL_DEFAULT_LOCALE'] = k2.default_language
  26 +app.config['BABEL_LANGUAGES'] = {
  27 + 'en': 'English',
  28 + 'uk': 'Українська',
  29 + 'pl': 'Polski'
  30 +}
  31 +app.config['BABEL_DEFAULT_TIMEZONE'] = k2.timezone
22 32 babel = Babel(app)
23 33 babel.init_app(app, locale_selector=k2.get_locale)
24 34  
... ... @@ -4,10 +4,4 @@
4 4 app.register_blueprint(components_bp)
5 5 from components.k2test.k2test.views import k2test
6 6 app.register_blueprint(k2test, url_prefix='/k2test')
7   -
8   -from components.k2auth.k2auth.views import k2auth
9   -app.register_blueprint(k2auth, url_prefix='/k2auth')
10   -
11   -from components.k2adm.k2adm.views import k2adm
12   -app.register_blueprint(k2adm, url_prefix='/k2adm')
... ... @@ -15,7 +15,7 @@
15 15 {% endblock %}
16 16 </head>
17 17 <body>
18   -
  18 +########################
19 19 <div class="content">
20 20 {% block content %}dashboard{% endblock %}
21 21 </div>
templates/component-info.html
1   -{% extends "base.html" %}
  1 +{% if 'base.html' in template_list %}
  2 + {% extends 'base.html' %}
  3 +{% else %}
  4 +
  5 +{% endif %}
  6 +
2 7 {% block styles %}
3 8 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
4 9  
templates/dashboard-add.html
  1 +{% extends 'base.html' %}
  2 +
  3 +
  4 +{% block styles %}
  5 +<style>
  6 + .table {
  7 + border-collapse: collapse;
  8 + width: 100%;
  9 + }
  10 +
  11 + .table th, .table td {
  12 + text-align: left;
  13 + padding: 8px;
  14 + border-bottom: 1px solid #ddd;
  15 + }
  16 +
  17 + .table tr:hover {
  18 + background-color: #f5f5f5;
  19 + }
  20 +
  21 + .search-form {
  22 + margin-bottom: 20px;
  23 + }
  24 +
  25 + .search-form .input-group {
  26 + width: 300px;
  27 + }
  28 +</style>
  29 +{% endblock %}
  30 +<!DOCTYPE html>
  31 +<html>
  32 +<head>
  33 + <title>{% block title %}{% endblock %}</title>
  34 + <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/style.css') }}">
  35 + <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/bootstrap.min.css') }}">
  36 + <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">
  37 + <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-kenU1KFdBIe4zVF0s0G1M5b4hcpxyD9F7jL+jjXkk+Q2h455rYXK/7HAuoJl+0I4" crossorigin="anonymous"></script>
  38 + <script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
  39 +
  40 +
  41 +</head>
  42 +<body>
  43 +
  44 + <div class="content">
  45 +{% block content %}
  46 +
  47 +<div class="container px-4 py-5" id="hanging-icons">
  48 + <h2 class="pb-2 border-bottom">{{gettext('t_components_shop')}}</h2>
  49 + <form action="{{ url_for('components.dashboard') }}" method="GET" class="search-form">
  50 + <div class="input-group">
  51 + <input type="text" name="search_query" class="form-control" placeholder="Search components">
  52 + <button type="submit" class="btn btn-primary">Search</button>
  53 + </div>
  54 +</form>
  55 +
  56 +
  57 + <div class="row g-4 py-5 row-cols-1 row-cols-lg-3">
  58 + {% if component_server %}
  59 + {% for component in component_server %}
  60 + <div class="col d-flex align-items-start">
  61 + <div class="icon-square bg-light text-dark flex-shrink-0 me-3">
  62 + <img class="d-block mx-auto mb-4" src="/static/img/logo-k2.png" alt="" width="60">
  63 + </div>
  64 + <div>
  65 + <h2>{{ component.name|title }}</h2>
  66 + <p>{{ component.description }}</p>
  67 + <p>ВЕРСІЯ {{ component.latest_version }}</p>
  68 + {% if component.name in components_names %}
  69 + <button type="button" class="btn btn-success">Installed</button>
  70 + {% else %}
  71 + <a href="" class="btn btn-primary">Install</a>
  72 + {% endif %}
  73 + </div>
  74 + </div>
  75 + {% endfor %}
  76 + {% else %}
  77 + <div class="col">
  78 + <p>No components available.</p>
  79 + </div>
  80 + {% endif %}
  81 + </div>
  82 +</div>
  83 +
  84 +{% endblock %}
  85 +
  86 +
  87 + </div>
  88 +
  89 +</body>
  90 +</html>
templates/dashboard-list.html
  1 + {% extends 'base.html' %}
  2 +
  3 +{% block styles %}
  4 +<style>
  5 + .table {
  6 + border-collapse: collapse;
  7 + width: 100%;
  8 + }
  9 +
  10 + .table th, .table td {
  11 + text-align: left;
  12 + padding: 8px;
  13 + border-bottom: 1px solid #ddd;
  14 + }
  15 +
  16 + .table tr:hover {
  17 + background-color: #f5f5f5;
  18 + }
  19 +
  20 + .search-form {
  21 + margin-bottom: 20px;
  22 + }
  23 +
  24 + .search-form .input-group {
  25 + width: 300px;
  26 + }
  27 +</style>
  28 +{% endblock %}
  29 +<!DOCTYPE html>
  30 +<html>
  31 +<head>
  32 + <title>{% block title %}{% endblock %}</title>
  33 + <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/style.css') }}">
  34 + <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/bootstrap.min.css') }}">
  35 + <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">
  36 + <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-kenU1KFdBIe4zVF0s0G1M5b4hcpxyD9F7jL+jjXkk+Q2h455rYXK/7HAuoJl+0I4" crossorigin="anonymous"></script>
  37 + <script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
  38 +
  39 +
  40 +</head>
  41 +<body>
  42 +
  43 + <div class="content">
  44 +{% block content %}
  45 +
  46 +<h2>{{ gettext('t_installed_components') }}</h2>
  47 +
  48 +<table class="table">
  49 + <thead>
  50 + <tr>
  51 + <th>{{gettext('t_name')}}</th>
  52 + <th>{{gettext('t_description')}}</th>
  53 + <th>{{gettext('t_version')}}</th>
  54 + <th>{{gettext('t_dependencies')}}</th>
  55 + <th>{{gettext('t_git_link')}}</th>
  56 + <th>{{gettext('t_install_at')}}</th>
  57 + <th>{{gettext('t_status')}}</th>
  58 + <th>{{gettext('t_action')}}</th>
  59 + </tr>
  60 + </thead>
  61 +
  62 + <tbody>
  63 + <tr>
  64 + <td>k2root</td>
  65 + <td>main component</td>
  66 + <td colspan="6">2.5.1.27</td></tr>
  67 +
  68 + {% for component in components %}
  69 + <tr>
  70 + <td>{{ component.name }}</td>
  71 + <td>{{ component.description }}</td>
  72 + <td>{{ component.version }}</td>
  73 + <td>{{ component.dependencies }}</td>
  74 + <td>{{ component.git_link }}</td>
  75 + <td>{{ component.date_installed }}</td>
  76 + <td>
  77 + {% if component.installed %}
  78 + Yes
  79 + {% else %}
  80 + No
  81 + {% endif %}
  82 + </td>
  83 + <td>
  84 + {% if component.installed %}
  85 + <a href="/remove_dependencies/{{ component.id }}" class="btn btn-primary">Вимкнути</a>
  86 + {% else %}
  87 + <a href="/add_dependencies/{{ component.id }}" class="btn btn-primary">Увімкнути</a>
  88 + <!-- Button trigger modal -->
  89 +<button type="button" class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#components-{{ component.id }}">
  90 + Видалити
  91 +</button>
  92 +
  93 +<!-- Modal -->
  94 +<div class="modal fade" id="components-{{ component.id }}" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
  95 + <div class="modal-dialog">
  96 + <div class="modal-content">
  97 + <div class="modal-header">
  98 + <h1 class="modal-title fs-5" id="exampleModalLabel">Modal title</h1>
  99 + <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
  100 + </div>
  101 + <div class="modal-body">
  102 + Видалити компоненту?
  103 + </div>
  104 + <div class="modal-footer">
  105 + <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
  106 + <a href="/remove-component/{{ component.id }}" class="btn btn-danger">Видалити</a>
  107 + </div>
  108 + </div>
  109 + </div>
  110 +</div>
  111 + {% endif %}
  112 + </td>
  113 + </tr>
  114 + {% endfor %}
  115 + </tbody>
  116 +</table>
  117 +
  118 +
  119 +
  120 +
  121 +{% endblock %}
  122 +
  123 +
  124 + </div>
  125 +
  126 +</body>
  127 +</html>
templates/dashboard.html
1   -{% extends "base.html" %}
2 1  
  2 +
3 3 {% block styles %}
4 4 <style>
5 5 .table {
6 6  
7 7  
8 8  
9 9  
10 10  
11 11  
... ... @@ -26,27 +26,40 @@
26 26 }
27 27 </style>
28 28 {% endblock %}
  29 +<!DOCTYPE html>
  30 +<html>
  31 +<head>
  32 + <title>{% block title %}{% endblock %}</title>
  33 + <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/style.css') }}">
  34 + <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/bootstrap.min.css') }}">
  35 + <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">
  36 + <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-kenU1KFdBIe4zVF0s0G1M5b4hcpxyD9F7jL+jjXkk+Q2h455rYXK/7HAuoJl+0I4" crossorigin="anonymous"></script>
  37 + <script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
29 38  
  39 +
  40 +</head>
  41 +<body>
  42 +
  43 + <div class="content">
30 44 {% block content %}
31 45 <h1>K2CloudERP-2.5.1.27</h1>
32   -<p> <a href="/logout">вихід</a></p>
33   -<p> <a href="{{ url_for('components.change_language', lang='en') }}">en</a> | <a href="{{ url_for('components.change_language', lang='uk') }}">uk</a></p>
  46 +<p> <a href="{{ url_for('components.change_language', lang='en') }}">en</a> | <a href="{{ url_for('components.change_language', lang='pl') }}">pl</a> | <a href="">uk</a></p>
34 47 {{language}}
35   -{{lang_d}}
36 48  
37   -<h2>{{ _('Installed components') }}</h2>
38 49  
  50 +<h2>{{ gettext('t_installed_components') }}</h2>
  51 +
39 52 <table class="table">
40 53 <thead>
41 54 <tr>
42   - <th>{{_('Name')}}</th>
43   - <th>{{_('Description')}}</th>
44   - <th>{{_('Version')}}</th>
45   - <th>{{_('Dependencies')}}</th>
46   - <th>{{_('Git Link')}}</th>
47   - <th>{{_('Install At')}}</th>
48   - <th>{{_('Status')}}</th>
49   - <th>{{_('Action')}}</th>
  55 + <th>{{gettext('t_name')}}</th>
  56 + <th>{{gettext('t_description')}}</th>
  57 + <th>{{gettext('t_version')}}</th>
  58 + <th>{{gettext('t_dependencies')}}</th>
  59 + <th>{{gettext('t_git_link')}}</th>
  60 + <th>{{gettext('t_install_at')}}</th>
  61 + <th>{{gettext('t_status')}}</th>
  62 + <th>{{gettext('t_action')}}</th>
50 63 </tr>
51 64 </thead>
52 65  
... ... @@ -112,7 +125,7 @@
112 125  
113 126  
114 127 <div class="container px-4 py-5" id="hanging-icons">
115   - <h2 class="pb-2 border-bottom">{{_('ADD Components')}}</h2>
  128 + <h2 class="pb-2 border-bottom">{{gettext('t_components_shop')}}</h2>
116 129 <form action="{{ url_for('components.dashboard') }}" method="GET" class="search-form">
117 130 <div class="input-group">
118 131 <input type="text" name="search_query" class="form-control" placeholder="Search components">
... ... @@ -149,4 +162,13 @@
149 162 </div>
150 163  
151 164 {% endblock %}
  165 +<br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/>
  166 + <footer>
  167 + &copy; 2023 Корпорація k2
  168 + </footer>
  169 +
  170 + </div>
  171 +
  172 +</body>
  173 +</html>