Commit fa42ef17428032c1f5c18a6f0badc1a680220fde

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

add k2site compatibility

Showing 43 changed files with 437 additions and 724 deletions Inline 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 File was deleted
2 __pycache__/
3 */__pycache__/
4 *.py[cod]
5 .pyc
6 1 idea
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" File was deleted
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" File was deleted
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 File was deleted
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
99 1 import uuid
components/k2adm/k2adm/views.py
1 import hashlib File was deleted
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
104 1 import hashlib
105 2 import uuid
components/k2adm/requirements.txt
1 alembic==1.11.1 File was deleted
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 File was deleted
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 )
16 1 from setuptools import setup
components/k2auth/.gitignore
1 idea File was deleted
2 __pycache__/
3 */__pycache__/
4 *.py[cod]
5 .pyc
6 .idea/
7 venv
8 1 idea
9 2 __pycache__/
components/k2auth/README.md
1 # main.py File was deleted
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 File was deleted
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
113 1
components/k2auth/k2auth/views.py
1 # import hashlib File was deleted
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)
67 1 # import hashlib
components/k2auth/requirements.txt
1 alembic==1.11.1 File was deleted
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
48 1 alembic==1.11.1
components/k2auth/setup.py
1 from setuptools import setup File was deleted
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 )
16 1 from setuptools import setup
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
1 from flask_sqlalchemy import SQLAlchemy 1 from flask_sqlalchemy import SQLAlchemy
2 from sqlalchemy import text 2 from sqlalchemy import text
3 from .k2obj import k2obj 3 from .k2obj import k2obj
4 import yaml 4 import yaml
5 import os 5 import os
6 import requests 6 import requests
7 import pytz 7 import pytz
8 import os 8 import os
9 import sys 9 import sys
10 from flask import session 10 from flask import session
11 11
12 12
13 13
14 class k2(k2obj): 14 class k2(k2obj):
15 version = '2.5.1.27' 15 version = '2.5.1.27'
16 db = SQLAlchemy() 16 db = SQLAlchemy()
17 domain = 'http://127.0.0.1:5000/' 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 venv_bin_path = os.path.join(os.path.dirname(sys.prefix), 'venv/Scripts') 19 venv_bin_path = os.path.join(os.path.dirname(sys.prefix), 'venv/Scripts')
20 json_sort_keys = False 20 json_sort_keys = False
21 timezone = pytz.timezone('Europe/Kiev') 21 timezone = pytz.timezone('Europe/Kiev')
22 secret_key = 'JH&INH987gFDHdsagh&8dwbjdw8ckw' 22 secret_key = 'JH&INH987gFDHdsagh&8dwbjdw8ckw'
23 default_language = 'en' 23 default_language = 'en'
24 current_language = '' 24 current_language = ''
25 babel_translation_directories = '' 25 babel_translation_directories = ''
26 components = '' 26 components = ''
27 27
28 28
29 29
30 def __init__(self): 30 def __init__(self):
31 super().__init__() 31 super().__init__()
32 32
33 @classmethod 33 @classmethod
34 def load_babel_translation_directories(cls): 34 def load_babel_translation_directories(cls):
35 with open('languages/babel_translation_directories.yml', 'r') as f: 35 with open('languages/babel_translation_directories.yml', 'r') as f:
36 data = yaml.safe_load(f) 36 data = yaml.safe_load(f)
37 print(data['babel_translation_directories'])
37 cls.babel_translation_directories = data['babel_translation_directories'] 38 cls.babel_translation_directories = data['babel_translation_directories']
38 39
39 @classmethod 40 @classmethod
40 def get_locale(cls): 41 def get_locale(cls):
41 if 'lang' in session: 42 if 'lang' in session:
42 # Отримати обрану мову з сеансу користувача 43 # Отримати обрану мову з сеансу користувача
43 return session['lang'] 44 return session['lang']
44 else: 45 else:
45 # Повернути мову за замовчуванням з налаштувань додатку 46 # Повернути мову за замовчуванням з налаштувань додатку
46 return cls.default_language #app.config['DEFAULT_LANGUAGE'] 47 return cls.default_language #app.config['DEFAULT_LANGUAGE']
47 48
48 @classmethod 49 @classmethod
49 def search_babel_translation_directories(cls): 50 def search_babel_translation_directories(cls):
50 response = requests.get(f'{cls.domain}api/languages') 51 response = requests.get(f'{cls.domain}api/languages')
51 if response.status_code == 200: 52 if response.status_code == 200:
52 cls.babel_translation_directories = response.text 53 cls.babel_translation_directories = response.text
53 data = {'babel_translation_directories': response.text} 54 data = {'babel_translation_directories': response.text}
54 else: 55 else:
55 cls.babel_translation_directories = 'languages' 56 cls.babel_translation_directories = 'languages'
56 data = {'babel_translation_directories': 'languages'} 57 data = {'babel_translation_directories': 'languages'}
57 58
58 with open('languages/babel_translation_directories.yml', 'w') as f: 59 with open('languages/babel_translation_directories.yml', 'w') as f:
59 yaml.dump(data, f) 60 yaml.dump(data, f)
60 61
61 # load database settings from yml file 62 # load database settings from yml file
62 def init_db_uri(self): 63 def init_db_uri(self):
63 with open('db/db.yml', 'r') as f: 64 with open('db/db.yml', 'r') as f:
64 db_config = yaml.load(f, Loader=yaml.FullLoader) 65 db_config = yaml.load(f, Loader=yaml.FullLoader)
65 return db_config 66 return db_config
66 67
67 # creating a connection 68 # creating a connection
68 def init_db(self, app): 69 def init_db(self, app):
69 db_config = self.init_db_uri() 70 db_config = self.init_db_uri()
70 db_uri = f"mysql+{db_config['driver']}://{db_config['user']}:{db_config['password']}@{db_config['host']}/{db_config['dbname']}" 71 db_uri = f"mysql+{db_config['driver']}://{db_config['user']}:{db_config['password']}@{db_config['host']}/{db_config['dbname']}"
71 app.config['SQLALCHEMY_DATABASE_URI'] = db_uri 72 app.config['SQLALCHEMY_DATABASE_URI'] = db_uri
72 db_path = '../db/database.db' 73 db_path = '../db/database.db'
73 app.config['SQLALCHEMY_BINDS'] = {'db2': f'sqlite:///{db_path}'} 74 app.config['SQLALCHEMY_BINDS'] = {'db2': f'sqlite:///{db_path}'}
74 self.db.init_app(app) 75 self.db.init_app(app)
75 76
76 77
1 import zipfile 1 import zipfile
2 from flask import Flask, Blueprint, jsonify, render_template, redirect, url_for, session, jsonify 2 from flask import Flask, Blueprint, jsonify, render_template, redirect, url_for, session, jsonify
3 import os 3 import os
4 import shutil 4 import shutil
5 from flask import request 5 from flask import request
6 from .k2cfg import k2 6 from .k2cfg import k2
7 from .k2comp import Component 7 from .k2comp import Component
8 from flask_migrate import Migrate 8 from flask_migrate import Migrate
9 import requests 9 import requests
10 import subprocess 10 import subprocess
11 from flask import current_app 11 from flask import current_app
12 import yaml 12 import yaml
13 from functools import wraps 13 from functools import wraps
14 import os 14 import os
15 import sys 15 import sys
16 from .k2admmenu import K2admin_menus, K2admin_Menus_Prava 16 from .k2admmenu import K2admin_menus, K2admin_Menus_Prava
17 from sqlalchemy import text 17 from sqlalchemy import text
18 from flask_jwt_extended import get_jwt_identity, jwt_required 18 from flask_jwt_extended import get_jwt_identity, jwt_required
19 19
20 20
21 21
22 # initialize db 22 # initialize db
23 # migrate = Migrate(app, db) 23 # migrate = Migrate(app, db)
24 24
25 components_bp = Blueprint('components', __name__, template_folder='templates') 25 components_bp = Blueprint('components', __name__, template_folder='templates')
26 config = yaml.safe_load(open("db/first-login.yml")) 26 config = yaml.safe_load(open("db/first-login.yml"))
27 27
28 db = k2.db 28 db = k2.db
29 29
30 30
31 def first_login_required(view_func): 31 def first_login_required(view_func):
32 @wraps(view_func) 32 @wraps(view_func)
33 def decorated_view(*args, **kwargs): 33 def decorated_view(*args, **kwargs):
34 if 'username' not in session: 34 if 'username' not in session:
35 return redirect(url_for('components.first_login')) 35 return redirect(url_for('components.first_login'))
36 return view_func(*args, **kwargs) 36 return view_func(*args, **kwargs)
37 37
38 return decorated_view 38 return decorated_view
39 39
40 # def append_to_yaml_file(file_path, data): 40 # def append_to_yaml_file(file_path, data):
41 # with open(file_path, 'r') as f: 41 # with open(file_path, 'r') as f:
42 # existing_data = yaml.safe_load(f) or [] 42 # existing_data = yaml.safe_load(f) or []
43 # 43 #
44 # if not existing_data: 44 # if not existing_data:
45 # start_id = 1 45 # start_id = 1
46 # else: 46 # else:
47 # start_id = max(item.get('id', 0) for item in existing_data) + 1 47 # start_id = max(item.get('id', 0) for item in existing_data) + 1
48 # 48 #
49 # for i, item in enumerate(data, start=start_id): 49 # for i, item in enumerate(data, start=start_id):
50 # item['id'] = i 50 # item['id'] = i
51 # existing_data.append(item) 51 # existing_data.append(item)
52 # 52 #
53 # with open(file_path, 'w') as f: 53 # with open(file_path, 'w') as f:
54 # yaml.dump(existing_data, f, default_flow_style=False) 54 # yaml.dump(existing_data, f, default_flow_style=False)
55 55
56 def remove_from_yaml_file(file_path, component_name): 56 def remove_from_yaml_file(file_path, component_name):
57 with open(file_path, 'r') as f: 57 with open(file_path, 'r') as f:
58 existing_data = yaml.safe_load(f) or [] 58 existing_data = yaml.safe_load(f) or []
59 59
60 updated_data = [item for item in existing_data if item.get('component_name') != component_name] 60 updated_data = [item for item in existing_data if item.get('component_name') != component_name]
61 61
62 with open(file_path, 'w') as f: 62 with open(file_path, 'w') as f:
63 yaml.dump(updated_data, f, default_flow_style=False) 63 yaml.dump(updated_data, f, default_flow_style=False)
64 64
65 65
66 @components_bp.route('/api/languages', methods=['GET']) 66 @components_bp.route('/api/languages', methods=['GET'])
67 def find_language(): 67 def find_language():
68 components = Component.query.all() 68 components = Component.query.all()
69 languages_paths = [] 69 languages_paths = []
70 # Додаємо головну директорію languages 70 # Додаємо головну директорію languages
71 languages_paths.append('languages') 71 languages_paths.append('languages')
72 # Знаходимо шляхи до директорій languages всередині папки components 72 # Знаходимо шляхи до директорій languages всередині папки components
73 for component in components: 73 for component in components:
74 component_languages_directory = 'components/' + component.name + '/' + component.name + '/languages' 74 component_languages_directory = 'components/' + component.name + '/' + component.name + '/languages'
75 languages_paths.append(component_languages_directory) 75 languages_paths.append(component_languages_directory)
76 result = ';'.join(languages_paths) 76 result = ';'.join(languages_paths)
77 print(result) 77 print(result)
78 return result 78 return result
79 79
80 80
81 @components_bp.route('/home', methods=['GET', 'POST']) 81 @components_bp.route('/', methods=['GET', 'POST'])
82 @first_login_required
83 def home(): 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 @components_bp.route('/first-login', methods=['GET', 'POST']) 92 @components_bp.route('/first-login', methods=['GET', 'POST'])
88 def first_login(): 93 def first_login():
89 if request.method == "POST": 94 if request.method == "POST":
90 username = request.form.get("username") 95 username = request.form.get("username")
91 password = request.form.get("password") 96 password = request.form.get("password")
92 for user in config["users"]: 97 for user in config["users"]:
93 if user["username"] == username and user["password"] == password: 98 if user["username"] == username and user["password"] == password:
94 session['username'] = username # Збереження ім'я користувача в сесії 99 session['username'] = username # Збереження ім'я користувача в сесії
95 return redirect(url_for('components.dashboard')) 100 return redirect(url_for('components.dashboard'))
96 return "Невірне ім'я користувача або пароль." 101 return "Невірне ім'я користувача або пароль."
97 return render_template('first-login.html') 102 return render_template('first-login.html')
98 103
99 104
100 @components_bp.route('/change_language/<lang>') 105 @components_bp.route('/change_language/<lang>')
101 def change_language(lang): 106 def change_language(lang):
102 session['lang'] = lang 107 session['lang'] = lang
103 k2.current_language = lang 108 k2.current_language = lang
104 return redirect(url_for('components.dashboard')) 109 return redirect(request.referrer)
105 110
106 111
107 @components_bp.route('/dashboard') 112 @components_bp.route('/dashboard')
108 @first_login_required
109 def dashboard(): 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 components = Component.query.all() 114 components = Component.query.all()
122 components_names = [component.name for component in components] 115 components_names = [component.name for component in components]
123 # Отримання значення пошукового запиту з параметрів URL 116 if 'k2site' in components_names:
124 search_query = request.args.get('search_query') 117 return redirect(url_for('components_bp.component_list_dasb')) # змінити на ендпоінт головної k2site
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)]
132 else: 118 else:
133 filtered_components = component_server 119 # Компоненти доступні для встановлення
134 current_language = k2.current_language 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, 129 # Встановлені компоненти
137 components_names=components_names, component_server=filtered_components, 130 components = Component.query.all()
138 search_query=search_query, language=k2.menu) 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 @components_bp.route('/show_components/<string:component_id>') 150 @components_bp.route('/show_components/<string:component_id>')
142 def show_components(component_id): 151 def show_components(component_id):
143 response = requests.get(f'{k2.update_domain}api/components') 152 response = requests.get(f'{k2.update_domain}api/components')
144 json_data = response.json() 153 json_data = response.json()
145 selected_component = next((component for component in json_data if component['id'] == component_id), None) 154 selected_component = next((component for component in json_data if component['id'] == component_id), None)
146 return render_template('component-info.html', selected_component=selected_component) 155 return render_template('component-info.html', selected_component=selected_component)
147 156
148 157
149 @components_bp.route('/install_components_git/<string:component_id>') 158 @components_bp.route('/install_components_git/<string:component_id>')
150 def install_components_git(component_id): 159 def install_components_git(component_id):
151 # Шлях до головної папки проекту 160 # Шлях до головної папки проекту
152 project_folder = 'components' 161 project_folder = 'components'
153 # component = Component.get_repository_by_id(component_id) 162 # component = Component.get_repository_by_id(component_id)
154 response = requests.get(f'{k2.update_domain}api/components') 163 response = requests.get(f'{k2.update_domain}api/components')
155 json_data = response.json() 164 json_data = response.json()
156 selected_component = next((component for component in json_data if component['id'] == component_id), None) 165 selected_component = next((component for component in json_data if component['id'] == component_id), None)
157 # Назва репозиторія 166 # Назва репозиторія
158 repository_name = selected_component['name'] 167 repository_name = selected_component['name']
159 # Шлях до папки репозиторія в межах проекту 168 # Шлях до папки репозиторія в межах проекту
160 repository_folder = os.path.join(project_folder, repository_name) 169 repository_folder = os.path.join(project_folder, repository_name)
161 # URL репозиторія 170 # URL репозиторія
162 171
163 git_repo_url = selected_component['git_link'] 172 git_repo_url = selected_component['git_link']
164 try: 173 try:
165 # Перевірка наявності папки репозиторія 174 # Перевірка наявності папки репозиторія
166 if not os.path.exists(repository_folder): 175 if not os.path.exists(repository_folder):
167 # Створення папки репозиторія 176 # Створення папки репозиторія
168 os.makedirs(repository_folder) 177 os.makedirs(repository_folder)
169 # Шлях до файлу __init__.py 178 # Шлях до файлу __init__.py
170 init_file = os.path.join(repository_folder, '__init__.py') 179 init_file = os.path.join(repository_folder, '__init__.py')
171 # Перевірка наявності файлу __init__.py 180 # Перевірка наявності файлу __init__.py
172 if not os.path.exists(init_file): 181 if not os.path.exists(init_file):
173 # Створення пустого файлу __init__.py 182 # Створення пустого файлу __init__.py
174 open(init_file, 'a').close() 183 open(init_file, 'a').close()
175 # підключення до приватного репозиторію 184 # підключення до приватного репозиторію
176 # os.environ['GITLAB_PRIVATE_TOKEN'] = '8xxTpxrKVDGoXD5ynjiW' 185 # os.environ['GITLAB_PRIVATE_TOKEN'] = '8xxTpxrKVDGoXD5ynjiW'
177 # git_repo_url_with_token = git_repo_url.replace('https://', 186 # git_repo_url_with_token = git_repo_url.replace('https://',
178 # f'https://oauth2:{os.environ["GITLAB_PRIVATE_TOKEN"]}@') 187 # f'https://oauth2:{os.environ["GITLAB_PRIVATE_TOKEN"]}@')
179 188
180 # Команда для встановлення з використанням git_repo_url і повного шляху до папки репозиторія 189 # Команда для встановлення з використанням git_repo_url і повного шляху до папки репозиторія
181 command = ['venv/Scripts/python.exe', '-m', 'pip', 'install', '--use-pep517', 'git+' + git_repo_url, 190 command = ['venv/Scripts/python.exe', '-m', 'pip', 'install', '--use-pep517', 'git+' + git_repo_url,
182 '--target=' + repository_folder] 191 '--target=' + repository_folder]
183 # Виконуємо команду встановлення 192 # Виконуємо команду встановлення
184 subprocess.check_call(command) 193 subprocess.check_call(command)
185 194
186 # Шлях до файлу, до якого потрібно додати код 195 # Шлях до файлу, до якого потрібно додати код
187 file_path = 'routes.py' 196 file_path = 'routes.py'
188 # Код, який потрібно додати 197 # Код, який потрібно додати
189 code = selected_component['dependencies'] 198 code = selected_component['dependencies']
190 # Відкриття файлу у режимі дозапису 199 # Відкриття файлу у режимі дозапису
191 with open(file_path, 'a') as file: 200 with open(file_path, 'a') as file:
192 # Запис нового коду у файл 201 # Запис нового коду у файл
193 file.write('\n') 202 file.write('\n')
194 file.write(code) 203 file.write(code)
195 file.write('\n') 204 file.write('\n')
196 component = Component.query.filter_by(name=selected_component['name']).first() 205 component = Component.query.filter_by(name=selected_component['name']).first()
197 if not component: 206 if not component:
198 new_component = Component( 207 new_component = Component(
199 name=selected_component['name'], 208 name=selected_component['name'],
200 description=selected_component['description'], 209 description=selected_component['description'],
201 version=selected_component['version'], 210 version=selected_component['version'],
202 git_link=selected_component['git_link'], 211 git_link=selected_component['git_link'],
203 dependencies=selected_component['dependencies'], 212 dependencies=selected_component['dependencies'],
204 installed=True 213 installed=True
205 ) 214 )
206 # Add the new component to the database 215 # Add the new component to the database
207 db.session.add(new_component) 216 db.session.add(new_component)
208 db.session.commit() 217 db.session.commit()
209 218
210 # Повертаємо повідомлення про успішне встановлення 219 # Повертаємо повідомлення про успішне встановлення
211 return f'Installed successfully <meta http-equiv="refresh" content="1;url=/dashboard" />' 220 return f'Installed successfully <meta http-equiv="refresh" content="1;url=/dashboard" />'
212 except subprocess.CalledProcessError as e: 221 except subprocess.CalledProcessError as e:
213 return 'Error installing : ' + str(e) 222 return 'Error installing : ' + str(e)
214 223
215 224
216 @components_bp.route('/install_components/<string:component_id>') 225 @components_bp.route('/install_components/<string:component_id>')
217 def install_component_from_archive(component_id): 226 def install_component_from_archive(component_id):
218 # Шлях до головної папки проекту 227 # Шлях до головної папки проекту
219 project_folder = 'components' 228 project_folder = 'components'
220 # Отримати відповідну компоненту зі списку компонент 229 # Отримати відповідну компоненту зі списку компонент
221 response = requests.get(f'{k2.update_domain}api/components') 230 response = requests.get(f'{k2.update_domain}api/components')
222 json_data = response.json() 231 json_data = response.json()
223 selected_component = next((component for component in json_data if component['id'] == component_id), None) 232 selected_component = next((component for component in json_data if component['id'] == component_id), None)
224 if selected_component is None: 233 if selected_component is None:
225 return 'Component not found' 234 return 'Component not found'
226 # Отримати посилання на архів компоненти та версію 235 # Отримати посилання на архів компоненти та версію
227 archive_url = selected_component['latest_component_data'] 236 archive_url = selected_component['latest_component_data']
228 version = selected_component['latest_version'] 237 version = selected_component['latest_version']
229 try: 238 try:
230 # Створити шлях до папки компоненти згідно назви та версії 239 # Створити шлях до папки компоненти згідно назви та версії
231 component_folder = os.path.join(project_folder) 240 component_folder = os.path.join(project_folder)
232 os.makedirs(component_folder, exist_ok=True) 241 os.makedirs(component_folder, exist_ok=True)
233 # Завантажити архів компоненти 242 # Завантажити архів компоненти
234 response = requests.get(archive_url, stream=True) 243 response = requests.get(archive_url, stream=True)
235 response.raise_for_status() 244 response.raise_for_status()
236 # Шлях до завантаженого архіву 245 # Шлях до завантаженого архіву
237 archive_path = os.path.join(component_folder, f"{selected_component['name']}.zip") 246 archive_path = os.path.join(component_folder, f"{selected_component['name']}.zip")
238 # Зберегти архів на диск 247 # Зберегти архів на диск
239 with open(archive_path, "wb") as file: 248 with open(archive_path, "wb") as file:
240 for chunk in response.iter_content(chunk_size=8192): 249 for chunk in response.iter_content(chunk_size=8192):
241 file.write(chunk) 250 file.write(chunk)
242 # Розпакувати архів 251 # Розпакувати архів
243 with zipfile.ZipFile(archive_path, "r") as zip_ref: 252 with zipfile.ZipFile(archive_path, "r") as zip_ref:
244 zip_ref.extractall(component_folder) 253 zip_ref.extractall(component_folder)
245 # Видалити архів 254 # Видалити архів
246 os.remove(archive_path) 255 os.remove(archive_path)
247 #перейменувати якщо git 256 #перейменувати якщо git
248 component_name = selected_component['name'] 257 component_name = selected_component['name']
249 old_folder_path = os.path.join(component_folder, component_name + ".git") 258 old_folder_path = os.path.join(component_folder, component_name + ".git")
250 new_folder_path = os.path.join(component_folder, component_name) 259 new_folder_path = os.path.join(component_folder, component_name)
251 # Перевірка наявності папки зі старою назвою 260 # Перевірка наявності папки зі старою назвою
252 if os.path.exists(old_folder_path) and os.path.isdir(old_folder_path): 261 if os.path.exists(old_folder_path) and os.path.isdir(old_folder_path):
253 # Перейменування папки зі старою назвою на нову назву 262 # Перейменування папки зі старою назвою на нову назву
254 os.rename(old_folder_path, new_folder_path) 263 os.rename(old_folder_path, new_folder_path)
255 264
256 # Шлях до файлу, до якого потрібно додати роути 265 # Шлях до файлу, до якого потрібно додати роути
257 file_path = 'routes.py' 266 file_path = 'routes.py'
258 # Код, який потрібно додати 267 # Код, який потрібно додати
259 code = f'''from components.{selected_component['name']}.{selected_component['name']}.views import {selected_component['name']}\napp.register_blueprint({selected_component['name']}, url_prefix='/{selected_component['name']}')''' 268 code = f'''from components.{selected_component['name']}.{selected_component['name']}.views import {selected_component['name']}\napp.register_blueprint({selected_component['name']}, url_prefix='/{selected_component['name']}')'''
260 # selected_component['dependencies'] 269 # selected_component['dependencies']
261 270
262 # Відкриття файлу у режимі дозапису 271 # Відкриття файлу у режимі дозапису
263 with open(file_path, 'a') as file: 272 with open(file_path, 'a') as file:
264 # Запис нового коду у файл 273 # Запис нового коду у файл
265 file.write('\n') 274 file.write('\n')
266 file.write(code) 275 file.write(code)
267 file.write('\n') 276 file.write('\n')
268 277
269 # Update database with the installed component 278 # Update database with the installed component
270 component = Component.query.filter_by(name=selected_component['name']).first() 279 component = Component.query.filter_by(name=selected_component['name']).first()
271 if not component: 280 if not component:
272 component = Component( 281 component = Component(
273 name=selected_component['name'], 282 name=selected_component['name'],
274 description=selected_component['description'], 283 description=selected_component['description'],
275 version=version, 284 version=version,
276 git_link=selected_component['git_link'], 285 git_link=selected_component['git_link'],
277 dependencies=code, 286 dependencies=code,
278 installed=True 287 installed=True
279 ) 288 )
280 db.session.add(component) 289 db.session.add(component)
281 else: 290 else:
282 component.version = version 291 component.version = version
283 component.git_link = selected_component['git_link'] 292 component.git_link = selected_component['git_link']
284 component.dependencies = code 293 component.dependencies = code
285 component.installed = True 294 component.installed = True
286 295
287 db.session.commit() 296 db.session.commit()
288 297
289 # add language folders 298 # add language folders
290
291 k2.search_babel_translation_directories() 299 k2.search_babel_translation_directories()
292 return f'''Component installed successfully: {selected_component["name"]} v{version} {k2.babel_translation_directories}, 300 return f'''Component installed successfully: {selected_component["name"]} v{version} {k2.babel_translation_directories},
293 \n \n please wait installing requirments... 301 \n \n please wait installing requirments...
294 <meta http-equiv="refresh" content="0;url=/install-requirments/{selected_component["name"]}" />''' 302 <meta http-equiv="refresh" content="0;url=/install-requirments/{selected_component["name"]}" />'''
295 303
296 except Exception as e: 304 except Exception as e:
297 return f'Error installing component: {str(e)}' 305 return f'Error installing component: {str(e)}'
298 306
299 307
300 @components_bp.route('/install-requirments/<string:selected_component_name>', methods=['GET']) 308 @components_bp.route('/install-requirments/<string:selected_component_name>', methods=['GET'])
301 def install_requirments(selected_component_name): 309 def install_requirments(selected_component_name):
302 # menu items 310 # menu items
303 get_admin_menu() 311 get_admin_menu()
304 312
305 313
306 # install requirements 314 # install requirements
307 requirements_file = os.path.join('components', selected_component_name, "requirements.txt") 315 requirements_file = os.path.join('components', selected_component_name, "requirements.txt")
308 if os.path.isfile(requirements_file): 316 if os.path.isfile(requirements_file):
309 pip_command = f"{k2.venv_bin_path}/python -m pip install -r {requirements_file}" 317 pip_command = f"{k2.venv_bin_path}/python -m pip install -r {requirements_file}"
310 subprocess.run(pip_command, shell=True, check=True) 318 subprocess.run(pip_command, shell=True, check=True)
311 return f'''Requirements installed successfully 319 return f'''Requirements installed successfully
312 \n \n please wait installing requirments components... 320 \n \n please wait installing requirments components...
313 <meta http-equiv="refresh" content="1;url=/install-requirments-components" />''' 321 <meta http-equiv="refresh" content="1;url=/install-requirments-components" />'''
314 322
315 323
316 @components_bp.route('/install-requirments-components') 324 @components_bp.route('/install-requirments-components')
317 def install_requirements_components(): 325 def install_requirements_components():
318 # Откриття файлу requirements_components.txt 326 # Откриття файлу requirements_components.txt
319 requirements_file = 'requirements_components.txt' 327 requirements_file = 'requirements_components.txt'
320 component_ids = None 328 component_ids = None
321 if os.path.isfile(requirements_file): 329 if os.path.isfile(requirements_file):
322 with open('requirements_components.txt', 'r') as file: 330 with open('requirements_components.txt', 'r') as file:
323 component_ids = file.read().splitlines() 331 component_ids = file.read().splitlines()
324 if component_ids: 332 if component_ids:
325 for component_id in component_ids: 333 for component_id in component_ids:
326 # Виклик роуту '/install_components/<string:component_name>' для кожної назви компоненти 334 # Виклик роуту '/install_components/<string:component_name>' для кожної назви компоненти
327 response = requests.get( 335 response = requests.get(
328 f'/install_components/{component_id}') 336 f'/install_components/{component_id}')
329 return f'Requirements components installed successfully <meta http-equiv="refresh" content="1;url=/dashboard" />' 337 return f'Requirements components installed successfully <meta http-equiv="refresh" content="1;url=/dashboard" />'
330 else: 338 else:
331 return f'<meta http-equiv="refresh" content="1;url=/dashboard" />' 339 return f'<meta http-equiv="refresh" content="1;url=/dashboard" />'
332 # Опрацювання відповіді (за потреби) 340 # Опрацювання відповіді (за потреби)
333 341
334 342
335 @components_bp.route('/remove_dependencies/<string:component_id>', methods=['GET']) 343 @components_bp.route('/remove_dependencies/<string:component_id>', methods=['GET'])
336 def remove_dependencies(component_id): 344 def remove_dependencies(component_id):
337 # Знаходимо компоненту за її ID 345 # Знаходимо компоненту за її ID
338 component = Component.query.get(component_id) 346 component = Component.query.get(component_id)
339 if not component: 347 if not component:
340 return 'Component not found <meta http-equiv="refresh" content="1;url=/dashboard" />' 348 return 'Component not found <meta http-equiv="refresh" content="1;url=/dashboard" />'
341 # remove menu items 349 # remove menu items
342 component_name = component.name 350 component_name = component.name
343 # file_path = "components/menu.yml" 351 # file_path = "components/menu.yml"
344 # remove_from_yaml_file(file_path, component_name) 352 # remove_from_yaml_file(file_path, component_name)
345 requests.get(f"{k2.domain}api/add-to-menu") 353 requests.get(f"{k2.domain}api/add-to-menu")
346 # remove routes 354 # remove routes
347 file_path = 'routes.py' 355 file_path = 'routes.py'
348 with open(file_path, 'r') as file: 356 with open(file_path, 'r') as file:
349 lines = file.readlines() 357 lines = file.readlines()
350 358
351 updated_lines = [line for line in lines if line.strip() not in component.dependencies] 359 updated_lines = [line for line in lines if line.strip() not in component.dependencies]
352 with open(file_path, 'w') as file: 360 with open(file_path, 'w') as file:
353 file.writelines(updated_lines) 361 file.writelines(updated_lines)
354 362
355 component.installed = False 363 component.installed = False
356 db.session.commit() 364 db.session.commit()
357 365
358 return 'Component successfully turn off <meta http-equiv="refresh" content="1;url=/dashboard" />' 366 return 'Component successfully turn off <meta http-equiv="refresh" content="1;url=/dashboard" />'
359 367
360 368
361 @components_bp.route('/add_dependencies/<string:component_id>', methods=['GET']) 369 @components_bp.route('/add_dependencies/<string:component_id>', methods=['GET'])
362 def add_dependencies(component_id): 370 def add_dependencies(component_id):
363 # Знаходимо компоненту за її ID 371 # Знаходимо компоненту за її ID
364 component = Component.query.get(component_id) 372 component = Component.query.get(component_id)
365 if not component: 373 if not component:
366 return 'Component not found <meta http-equiv="refresh" content="1;url=/dashboard" />' 374 return 'Component not found <meta http-equiv="refresh" content="1;url=/dashboard" />'
367 375
368 file_path = 'routes.py' 376 file_path = 'routes.py'
369 code = component.dependencies 377 code = component.dependencies
370 # Відкриття файлу у режимі дозапису 378 # Відкриття файлу у режимі дозапису
371 with open(file_path, 'a') as file: 379 with open(file_path, 'a') as file:
372 # Запис нового коду у файл 380 # Запис нового коду у файл
373 file.write(code) 381 file.write(code)
374 382
375 # Оновлюємо статус компоненти 383 # Оновлюємо статус компоненти
376 component.installed = True 384 component.installed = True
377 db.session.commit() 385 db.session.commit()
378 return 'Dependencies added successfully <meta http-equiv="refresh" content="1;url=/dashboard" />' 386 return 'Dependencies added successfully <meta http-equiv="refresh" content="1;url=/dashboard" />'
379 387
380 388
381 @components_bp.route('/remove-component/<string:component_id>') 389 @components_bp.route('/remove-component/<string:component_id>')
382 def remove_component(component_id): 390 def remove_component(component_id):
383 # Шлях до головної папки проекту 391 # Шлях до головної папки проекту
384 project_folder = 'components' 392 project_folder = 'components'
385 component = Component.query.get(component_id) 393 component = Component.query.get(component_id)
386 # Назва репозиторія 394 # Назва репозиторія
387 repository_name = component.name 395 repository_name = component.name
388 # Шлях до папки репозиторія в межах проекту 396 # Шлях до папки репозиторія в межах проекту
389 repository_folder = os.path.join(project_folder, repository_name) 397 repository_folder = os.path.join(project_folder, repository_name)
390 try: 398 try:
391 # Перевірка наявності папки репозиторія 399 # Перевірка наявності папки репозиторія
392 if os.path.exists(repository_folder): 400 if os.path.exists(repository_folder):
393 # Видалення папки репозиторія 401 # Видалення папки репозиторія
394 shutil.rmtree(repository_folder) 402 shutil.rmtree(repository_folder)
395 # Видаляємо компоненту з бази 403 # Видаляємо компоненту з бази
396 if component: 404 if component:
397 db.session.delete(component) 405 db.session.delete(component)
398 db.session.commit() 406 db.session.commit()
399 407
400 # Повертаємо повідомлення про успішне видалення 408 # Повертаємо повідомлення про успішне видалення
401 return 'Component removed successfully <meta http-equiv="refresh" content="1;url=/dashboard" />' 409 return 'Component removed successfully <meta http-equiv="refresh" content="1;url=/dashboard" />'
402 except Exception as e: 410 except Exception as e:
403 # Повертаємо повідомлення про помилку видалення 411 # Повертаємо повідомлення про помилку видалення
404 return 'Error removing component: ' + str(e) 412 return 'Error removing component: ' + str(e)
405 413
406 414
407 @components_bp.route('/component/add') 415 # @components_bp.route('/component/add')
408 def component_add(): 416 # def component_add():
409 # components for install 417 # # components for install
410 try: 418 # try:
411 # GET-requests to API 419 # # GET-requests to API
412 response = requests.get(f'{k2.update_domain}api/components') 420 # response = requests.get(f'{k2.update_domain}api/components')
413 json_data = response.json() 421 # json_data = response.json()
414 for item in json_data: 422 # for item in json_data:
415 item['button'] = f"{k2.domain}/install_components/{item['id']}" 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 # menu 448 # menu
441 @components_bp.route('/api/add-to-menu', methods=['GET']) 449 @components_bp.route('/api/add-to-menu', methods=['GET'])
442 def add_to_menu(): 450 def add_to_menu():
443 data = [] 451 data = []
444 response = requests.get(f"{k2.domain}/menu-admin-items") 452 response = requests.get(f"{k2.domain}/menu-admin-items")
445 data.append(response.json()[0]) 453 data.append(response.json()[0])
446 prev_menu = response.json()[0]['title'] 454 prev_menu = response.json()[0]['title']
447 for item in response.json()[0]['children']: 455 for item in response.json()[0]['children']:
448 name_menu = item['to'] 456 name_menu = str(item['to']).replace('/','-')
449 caption_menu = item['title'] 457 caption_menu = item['title']
450 add_menu_with_permissions_db(name_menu, prev_menu, caption_menu) 458 add_menu_with_permissions_db(name_menu, prev_menu, caption_menu)
451 components = Component.query.all() 459 components = Component.query.all()
452 components_names = [component.name for component in components] 460 components_names = [component.name for component in components]
453 for components_names in components_names: 461 for components_names in components_names:
454 response = requests.get(f"{k2.domain}{components_names}/menu-admin-items") 462 response = requests.get(f"{k2.domain}{components_names}/menu-admin-items")
455 if response.status_code == 200: 463 if response.status_code == 200:
456 data.append(response.json()[0]) 464 data.append(response.json()[0])
457 #prev_menu = 465 #prev_menu =
458 prev_menu = response.json()[0]['title'] 466 prev_menu = response.json()[0]['title']
459 for item in response.json()[0]['children']: 467 for item in response.json()[0]['children']:
460 name_menu = item['to'] 468 name_menu = item['to']
461 caption_menu = item['title'] 469 caption_menu = item['title']
462 add_menu_with_permissions_db(name_menu, prev_menu, caption_menu) 470 add_menu_with_permissions_db(name_menu, prev_menu, caption_menu)
463 else: 471 else:
464 data 472 data
465 # add component_name key for menu items 473 # add component_name key for menu items
466 for item in data: 474 for item in data:
467 item['component_name'] = components_names 475 item['component_name'] = components_names
468 k2.menu = data 476 k2.menu = data
469 return k2.menu 477 return k2.menu
470 478
471 479
472 @components_bp.route('/api/main-menu/') 480 @components_bp.route('/api/main-menu/')
473 @jwt_required() 481 @jwt_required()
474 def get_admin_menu(): 482 def get_admin_menu():
475 current_user = get_jwt_identity() 483 current_user = get_jwt_identity()
476 role_id = current_user['user_role'] 484 role_id = current_user['user_role']
477 filter_menu = [] 485 filter_menu = []
478 response = requests.get(f"{k2.domain}api/add-to-menu") 486 response = requests.get(f"{k2.domain}api/add-to-menu")
479 menu_items_list = K2admin_menus.get_menu_items_by_roles(role_id) 487 menu_items_list = K2admin_menus.get_menu_items_by_roles(role_id)
480 488
481 for item in response.json(): 489 for item in response.json():
482 filtered_children = [] 490 filtered_children = []
483 for im in item['children']: 491 for im in item['children']:
484 492
485 if im['to'] in menu_items_list: 493 if im['to'] in menu_items_list:
486 filtered_children.append(im) 494 filtered_children.append(im)
487 if filtered_children: 495 if filtered_children:
488 item['children'] = filtered_children 496 item['children'] = filtered_children
489 filter_menu.append(item) 497 filter_menu.append(item)
490 filter_menu.append({"heading": 'APPS & PAGES'}) 498 filter_menu.append({"heading": 'APPS & PAGES'})
491 499
492 return jsonify(filter_menu) 500 return jsonify(filter_menu)
493 501
494 502
495 def add_menu_with_permissions_db(name_menu, prev_menu, caption_menu): 503 def add_menu_with_permissions_db(name_menu, prev_menu, caption_menu):
496 504
497 # Створення об'єкта k2admin_menus 505 # Створення об'єкта k2admin_menus
498 menu = text('SELECT COUNT(*) FROM k2admin_menus WHERE namemenu = :name_menu') 506 menu = text('SELECT COUNT(*) FROM k2admin_menus WHERE namemenu = :name_menu')
499 result = db.session.execute(menu, {'name_menu': name_menu}).fetchone() 507 result = db.session.execute(menu, {'name_menu': name_menu}).fetchone()
500 count = result[0] 508 count = result[0]
501 if count == 0: 509 if count == 0:
502 new_menu_element = K2admin_menus( 510 new_menu_element = K2admin_menus(
503 namemenu=name_menu, 511 namemenu=name_menu,
504 prevmenu=prev_menu, 512 prevmenu=prev_menu,
505 caption=caption_menu, 513 caption=caption_menu,
506 module_name=name_menu 514 module_name=name_menu
507 ) 515 )
508 # Додавання об'єкта k2admin_menus до сесії
509 db.session.add(new_menu_element) 516 db.session.add(new_menu_element)
510 db.session.commit() 517 db.session.commit()
511 # Створення об'єкта k2admin_menus_prava 518 # Створення об'єкта k2admin_menus_prava
512 new_prava = K2admin_Menus_Prava( 519 new_prava = K2admin_Menus_Prava(
513 menuid=new_menu_element.menuid, # Зв'язуємо зовнішній ключ з menuid нового меню 520 menuid=new_menu_element.menuid, # Зв'язуємо зовнішній ключ з menuid нового меню
514 username=None, 521 username=None,
515 r=0, 522 r=0,
516 w=0, 523 w=0,
517 i=0, 524 i=0,
518 d=0, 525 d=0,
519 c=0, 526 c=0,
520 exp=0, 527 exp=0,
521 imp=0, 528 imp=0,
522 settable=0, 529 settable=0,
523 cutpast=0, 530 cutpast=0,
524 enable=0, 531 enable=0,
525 roleid=-1 532 roleid=-1
526 ) 533 )
527 # Додавання об'єкта k2admin_menus_prava до сесії 534 # Додавання об'єкта k2admin_menus_prava до сесії
528 db.session.add(new_prava) 535 db.session.add(new_prava)
529 db.session.commit() 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
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 msgstr "Installed components" 2 msgstr "Installed components"
3 3
4 msgid "Name" 4 msgid "t_name"
5 msgstr "Name" 5 msgstr "Name"
6 6
7 msgid "Description" 7 msgid "t_description"
8 msgstr "Description" 8 msgstr "Description"
9 9
10 msgid "Version" 10 msgid "t_version"
11 msgstr "Version" 11 msgstr "Version"
12 12
13 msgid "Dependencies" 13 msgid "t_dependencies"
14 msgstr "Dependencies" 14 msgstr "Dependencies"
15 15
16 msgid "Git Link" 16 msgid "t_git_link"
17 msgstr "Git Link" 17 msgstr "Git Link"
18 18
19 msgid "Install At" 19 msgid "t_install_at"
20 msgstr "Install At" 20 msgstr "Install At"
21 21
22 msgid "Status" 22 msgid "t_status"
23 msgstr "Status" 23 msgstr "Status"
24 24
25 msgid "Action" 25 msgid "t_action"
26 msgstr "Action" 26 msgstr "Action"
27 27
28 msgid "Components Shop" 28 msgid "t_components_shop"
29 msgstr "Components Shop" 29 msgstr "Add component"
30
31
languages/pl/LC_MESSAGES/messages.mo
No preview for this file type
languages/pl/LC_MESSAGES/messages.po
File was created 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" 1 msgid ""
2 msgstr "Встановлені компоненти" 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 msgstr "Встановлені компоненти" 7 msgstr "Встановлені компоненти"
6 8
7 msgid "Name" 9 msgid "t_name"
8 msgstr "Назва" 10 msgstr "Назва"
9 11
10 msgid "Description" 12 msgid "t_description"
11 msgstr "Опис" 13 msgstr "Опис"
12 14
13 msgid "Version" 15 msgid "t_version"
14 msgstr "Версія" 16 msgstr "Версія"
15 17
16 msgid "Dependencies" 18 msgid "t_dependencies"
17 msgstr "Залежності" 19 msgstr "Залежності"
18 20
19 msgid "Git Link" 21 msgid "t_git_link"
20 msgstr "Посилання на Git" 22 msgstr "Посилання на Git"
21 23
22 msgid "Install At" 24 msgid "t_install_at"
23 msgstr "Встановлено" 25 msgstr "Встановлено"
24 26
25 msgid "Status" 27 msgid "t_status"
26 msgstr "Статус" 28 msgstr "Статус"
27 29
28 msgid "Action" 30 msgid "t_action"
29 msgstr "Дія" 31 msgstr "Дія"
30 32
31 msgid "Components Shop" 33 msgid "t_components_shop"
32 msgstr "Магазин компонент" 34 msgstr "Додати компоненту"
1 from flask_cors import CORS 1 from flask_cors import CORS
2 from flask_babel import Babel 2 from flask_babel import Babel
3 from flask_jwt_extended import JWTManager 3 from flask_jwt_extended import JWTManager
4 #from k2.k2inst import * 4 #from k2.k2rout import *
5 from flask import Flask 5 from flask import Flask
6 from k2.k2cfg import k2 6 from k2.k2cfg import k2
7 7
8
9
8 # configure flask app 10 # configure flask app
9 app = Flask(__name__) 11 app = Flask(__name__)
10 app.config['SECRET_KEY'] = k2.secret_key 12 app.config['SECRET_KEY'] = k2.secret_key
11 app.json.sort_keys = k2.json_sort_keys 13 app.json.sort_keys = k2.json_sort_keys
12 #app.config.from_object(k2) 14 #app.config.from_object(k2)
13 15
16
14 # init db 17 # init db
15 k2().init_db(app) 18 k2().init_db(app)
16 CORS(app) 19 CORS(app)
17 jwt = JWTManager(app) 20 jwt = JWTManager(app)
18 21
19 #translate 22 #translate
20 k2.load_babel_translation_directories() 23 k2.load_babel_translation_directories()
21 app.config['BABEL_TRANSLATION_DIRECTORIES'] = k2.babel_translation_directories 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 babel = Babel(app) 32 babel = Babel(app)
23 babel.init_app(app, locale_selector=k2.get_locale) 33 babel.init_app(app, locale_selector=k2.get_locale)
24 34
25 # flask routing 35 # flask routing
26 from routes import * 36 from routes import *
27 37
28 if __name__ == '__main__': 38 if __name__ == '__main__':
29 app.run(debug=True, port=5000) 39 app.run(debug=True, port=5000)
30 40
31 41
32 42
33 43
34 44
35 45
1 #app = Flask(__name__) 1 #app = Flask(__name__)
2 from main import app 2 from main import app
3 from k2.k2rout import components_bp 3 from k2.k2rout import components_bp
4 app.register_blueprint(components_bp) 4 app.register_blueprint(components_bp)
5 from components.k2test.k2test.views import k2test 5 from components.k2test.k2test.views import k2test
6 app.register_blueprint(k2test, url_prefix='/k2test') 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')
13
1 <!DOCTYPE html> 1 <!DOCTYPE html>
2 <html> 2 <html>
3 <head> 3 <head>
4 <title>{% block title %}{% endblock %}</title> 4 <title>{% block title %}{% endblock %}</title>
5 <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/style.css') }}"> 5 <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/style.css') }}">
6 <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/bootstrap.min.css') }}"> 6 <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/bootstrap.min.css') }}">
7 <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous"> 7 <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">
8 <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> 8 <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>
9 <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> 9 <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>
10 10
11 11
12 12
13 {% block styles %} 13 {% block styles %}
14 <!-- Підключення CSS-стилів --> 14 <!-- Підключення CSS-стилів -->
15 {% endblock %} 15 {% endblock %}
16 </head> 16 </head>
17 <body> 17 <body>
18 18 ########################
19 <div class="content"> 19 <div class="content">
20 {% block content %}dashboard{% endblock %} 20 {% block content %}dashboard{% endblock %}
21 </div> 21 </div>
22 <br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/> 22 <br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/>
23 <footer> 23 <footer>
24 &copy; 2023 Корпорація k2 24 &copy; 2023 Корпорація k2
25 </footer> 25 </footer>
26 26
27 27
28 {% block scripts %} 28 {% block scripts %}
29 <!-- Підключення скриптів JavaScript --> 29 <!-- Підключення скриптів JavaScript -->
30 {% endblock %} 30 {% endblock %}
31 </body> 31 </body>
32 </html> 32 </html>
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 {% block styles %} 7 {% block styles %}
3 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css"> 8 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
4 9
5 <style> 10 <style>
6 .bd-placeholder-img { 11 .bd-placeholder-img {
7 font-size: 1.125rem; 12 font-size: 1.125rem;
8 text-anchor: top; 13 text-anchor: top;
9 -webkit-user-select: none; 14 -webkit-user-select: none;
10 -moz-user-select: none; 15 -moz-user-select: none;
11 user-select: none; 16 user-select: none;
12 } 17 }
13 18
14 @media (min-width: 768px) { 19 @media (min-width: 768px) {
15 .bd-placeholder-img-lg { 20 .bd-placeholder-img-lg {
16 font-size: 3.5rem; 21 font-size: 3.5rem;
17 } 22 }
18 } 23 }
19 24
20 .b-example-divider { 25 .b-example-divider {
21 height: 3rem; 26 height: 3rem;
22 background-color: rgba(0, 0, 0, .1); 27 background-color: rgba(0, 0, 0, .1);
23 border: solid rgba(0, 0, 0, .15); 28 border: solid rgba(0, 0, 0, .15);
24 border-width: 1px 0; 29 border-width: 1px 0;
25 box-shadow: inset 0 .5em 1.5em rgba(0, 0, 0, .1), inset 0 .125em .5em rgba(0, 0, 0, .15); 30 box-shadow: inset 0 .5em 1.5em rgba(0, 0, 0, .1), inset 0 .125em .5em rgba(0, 0, 0, .15);
26 } 31 }
27 32
28 @media (min-width: 992px) { 33 @media (min-width: 992px) {
29 .rounded-lg-3 { border-radius: .3rem; } 34 .rounded-lg-3 { border-radius: .3rem; }
30 } 35 }
31 </style> 36 </style>
32 37
33 38
34 {% endblock %} 39 {% endblock %}
35 {% block content %} 40 {% block content %}
36 <div id="popupBackdrop"> 41 <div id="popupBackdrop">
37 <div class="loader"> 42 <div class="loader">
38 Встановлення компоненти... <i class="fa-solid fa-cog fa-spin fa-xl"></i> 43 Встановлення компоненти... <i class="fa-solid fa-cog fa-spin fa-xl"></i>
39 </div> 44 </div>
40 </div> 45 </div>
41 46
42 {% if selected_component %} 47 {% if selected_component %}
43 <div class="text-center border-bottom"> 48 <div class="text-center border-bottom">
44 <img class="d-block mx-auto mb-4" src="{{ url_for('static', filename='img/logo-k2.png')}}" alt="" width="200"> 49 <img class="d-block mx-auto mb-4" src="{{ url_for('static', filename='img/logo-k2.png')}}" alt="" width="200">
45 <h3 class="display-4 fw-bold">K2CloudERP-2.5.1.27</h3> 50 <h3 class="display-4 fw-bold">K2CloudERP-2.5.1.27</h3>
46 <h1 class="display-4 fw-bold">{{selected_component.name|title}}</h1> 51 <h1 class="display-4 fw-bold">{{selected_component.name|title}}</h1>
47 <div class="col-lg-6 mx-auto"> 52 <div class="col-lg-6 mx-auto">
48 <p class="lead mb-4">{{selected_component.description|title}} - 53 <p class="lead mb-4">{{selected_component.description|title}} -
49 quickly design and customize responsive mobile-first components with Python and Bootstrap. 54 quickly design and customize responsive mobile-first components with Python and Bootstrap.
50 </p> 55 </p>
51 <p class="lead mb-4">Посилання на репозиторій: {{selected_component.git_link}}</p> 56 <p class="lead mb-4">Посилання на репозиторій: {{selected_component.git_link}}</p>
52 <p class="lead mb-4">Версія: {{selected_component.version}}</p> 57 <p class="lead mb-4">Версія: {{selected_component.version}}</p>
53 <p class="lead mb-4">Розробка: K2 Cloud</p> 58 <p class="lead mb-4">Розробка: K2 Cloud</p>
54 <div class="d-grid gap-2 d-sm-flex justify-content-sm-center mb-5"> 59 <div class="d-grid gap-2 d-sm-flex justify-content-sm-center mb-5">
55 60
56 <a href="/install_components/{{ selected_component.id }}" id="installBtn" class="btn btn-primary btn-lg px-4 me-sm-3" onclick="showLoader()">Встановити компоненту</a> 61 <a href="/install_components/{{ selected_component.id }}" id="installBtn" class="btn btn-primary btn-lg px-4 me-sm-3" onclick="showLoader()">Встановити компоненту</a>
57 </div> 62 </div>
58 </div> 63 </div>
59 <div class="overflow-hidden" style="max-height: 30vh;"> 64 <div class="overflow-hidden" style="max-height: 30vh;">
60 <div class="container px-5"> 65 <div class="container px-5">
61 <img src="https://corp2.eu/media/assets/images/service/k2cloud.jpg" class="img-fluid border rounded-3 shadow-lg mb-4" alt="Example image" width="700" height="500" loading="lazy"> 66 <img src="https://corp2.eu/media/assets/images/service/k2cloud.jpg" class="img-fluid border rounded-3 shadow-lg mb-4" alt="Example image" width="700" height="500" loading="lazy">
62 </div> 67 </div>
63 </div> 68 </div>
64 </div> 69 </div>
65 70
66 {% else %} 71 {% else %}
67 Помилка завантаження компоненти з сервера 72 Помилка завантаження компоненти з сервера
68 {% endif %} 73 {% endif %}
69 74
70 <script> 75 <script>
71 function showLoader() { 76 function showLoader() {
72 document.getElementById("popupBackdrop").style.display = "block"; 77 document.getElementById("popupBackdrop").style.display = "block";
73 } 78 }
74 79
75 function hideLoader() { 80 function hideLoader() {
76 document.getElementById("popupBackdrop").style.display = "none"; 81 document.getElementById("popupBackdrop").style.display = "none";
77 } 82 }
78 83
79 window.onload = function() { 84 window.onload = function() {
80 hideLoader(); 85 hideLoader();
81 }; 86 };
82 </script> 87 </script>
83 88
84 89
85 {% endblock %} 90 {% endblock %}
86 91
87 92
templates/dashboard-add.html
File was created 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
File was created 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 {% block styles %} 3 {% block styles %}
4 <style> 4 <style>
5 .table { 5 .table {
6 border-collapse: collapse; 6 border-collapse: collapse;
7 width: 100%; 7 width: 100%;
8 } 8 }
9 9
10 .table th, .table td { 10 .table th, .table td {
11 text-align: left; 11 text-align: left;
12 padding: 8px; 12 padding: 8px;
13 border-bottom: 1px solid #ddd; 13 border-bottom: 1px solid #ddd;
14 } 14 }
15 15
16 .table tr:hover { 16 .table tr:hover {
17 background-color: #f5f5f5; 17 background-color: #f5f5f5;
18 } 18 }
19 19
20 .search-form { 20 .search-form {
21 margin-bottom: 20px; 21 margin-bottom: 20px;
22 } 22 }
23 23
24 .search-form .input-group { 24 .search-form .input-group {
25 width: 300px; 25 width: 300px;
26 } 26 }
27 </style> 27 </style>
28 {% endblock %} 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 {% block content %} 44 {% block content %}
31 <h1>K2CloudERP-2.5.1.27</h1> 45 <h1>K2CloudERP-2.5.1.27</h1>
32 <p> <a href="/logout">вихід</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>
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>
34 {{language}} 47 {{language}}
35 {{lang_d}}
36 48
37 <h2>{{ _('Installed components') }}</h2>
38 49
50 <h2>{{ gettext('t_installed_components') }}</h2>
51
39 <table class="table"> 52 <table class="table">
40 <thead> 53 <thead>
41 <tr> 54 <tr>
42 <th>{{_('Name')}}</th> 55 <th>{{gettext('t_name')}}</th>
43 <th>{{_('Description')}}</th> 56 <th>{{gettext('t_description')}}</th>
44 <th>{{_('Version')}}</th> 57 <th>{{gettext('t_version')}}</th>
45 <th>{{_('Dependencies')}}</th> 58 <th>{{gettext('t_dependencies')}}</th>
46 <th>{{_('Git Link')}}</th> 59 <th>{{gettext('t_git_link')}}</th>
47 <th>{{_('Install At')}}</th> 60 <th>{{gettext('t_install_at')}}</th>
48 <th>{{_('Status')}}</th> 61 <th>{{gettext('t_status')}}</th>
49 <th>{{_('Action')}}</th> 62 <th>{{gettext('t_action')}}</th>
50 </tr> 63 </tr>
51 </thead> 64 </thead>
52 65
53 <tbody> 66 <tbody>
54 <tr> 67 <tr>
55 <td>k2root</td> 68 <td>k2root</td>
56 <td>main component</td> 69 <td>main component</td>
57 <td colspan="6">2.5.1.27</td></tr> 70 <td colspan="6">2.5.1.27</td></tr>
58 71
59 {% for component in components %} 72 {% for component in components %}
60 <tr> 73 <tr>
61 <td>{{ component.name }}</td> 74 <td>{{ component.name }}</td>
62 <td>{{ component.description }}</td> 75 <td>{{ component.description }}</td>
63 <td>{{ component.version }}</td> 76 <td>{{ component.version }}</td>
64 <td>{{ component.dependencies }}</td> 77 <td>{{ component.dependencies }}</td>
65 <td>{{ component.git_link }}</td> 78 <td>{{ component.git_link }}</td>
66 <td>{{ component.date_installed }}</td> 79 <td>{{ component.date_installed }}</td>
67 <td> 80 <td>
68 {% if component.installed %} 81 {% if component.installed %}
69 Yes 82 Yes
70 {% else %} 83 {% else %}
71 No 84 No
72 {% endif %} 85 {% endif %}
73 </td> 86 </td>
74 <td> 87 <td>
75 {% if component.installed %} 88 {% if component.installed %}
76 <a href="/remove_dependencies/{{ component.id }}" class="btn btn-primary">Вимкнути</a> 89 <a href="/remove_dependencies/{{ component.id }}" class="btn btn-primary">Вимкнути</a>
77 {% else %} 90 {% else %}
78 <a href="/add_dependencies/{{ component.id }}" class="btn btn-primary">Увімкнути</a> 91 <a href="/add_dependencies/{{ component.id }}" class="btn btn-primary">Увімкнути</a>
79 <!-- Button trigger modal --> 92 <!-- Button trigger modal -->
80 <button type="button" class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#components-{{ component.id }}"> 93 <button type="button" class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#components-{{ component.id }}">
81 Видалити 94 Видалити
82 </button> 95 </button>
83 96
84 <!-- Modal --> 97 <!-- Modal -->
85 <div class="modal fade" id="components-{{ component.id }}" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true"> 98 <div class="modal fade" id="components-{{ component.id }}" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
86 <div class="modal-dialog"> 99 <div class="modal-dialog">
87 <div class="modal-content"> 100 <div class="modal-content">
88 <div class="modal-header"> 101 <div class="modal-header">
89 <h1 class="modal-title fs-5" id="exampleModalLabel">Modal title</h1> 102 <h1 class="modal-title fs-5" id="exampleModalLabel">Modal title</h1>
90 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 103 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
91 </div> 104 </div>
92 <div class="modal-body"> 105 <div class="modal-body">
93 Видалити компоненту? 106 Видалити компоненту?
94 </div> 107 </div>
95 <div class="modal-footer"> 108 <div class="modal-footer">
96 <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button> 109 <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
97 <a href="/remove-component/{{ component.id }}" class="btn btn-danger">Видалити</a> 110 <a href="/remove-component/{{ component.id }}" class="btn btn-danger">Видалити</a>
98 </div> 111 </div>
99 </div> 112 </div>
100 </div> 113 </div>
101 </div> 114 </div>
102 {% endif %} 115 {% endif %}
103 </td> 116 </td>
104 </tr> 117 </tr>
105 {% endfor %} 118 {% endfor %}
106 </tbody> 119 </tbody>
107 </table> 120 </table>
108 121
109 122
110 123
111 124
112 125
113 126
114 <div class="container px-4 py-5" id="hanging-icons"> 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 <form action="{{ url_for('components.dashboard') }}" method="GET" class="search-form"> 129 <form action="{{ url_for('components.dashboard') }}" method="GET" class="search-form">
117 <div class="input-group"> 130 <div class="input-group">
118 <input type="text" name="search_query" class="form-control" placeholder="Search components"> 131 <input type="text" name="search_query" class="form-control" placeholder="Search components">
119 <button type="submit" class="btn btn-primary">Search</button> 132 <button type="submit" class="btn btn-primary">Search</button>
120 </div> 133 </div>
121 </form> 134 </form>
122 135
123 136
124 <div class="row g-4 py-5 row-cols-1 row-cols-lg-3"> 137 <div class="row g-4 py-5 row-cols-1 row-cols-lg-3">
125 {% if component_server %} 138 {% if component_server %}
126 {% for component in component_server %} 139 {% for component in component_server %}
127 <div class="col d-flex align-items-start"> 140 <div class="col d-flex align-items-start">
128 <div class="icon-square bg-light text-dark flex-shrink-0 me-3"> 141 <div class="icon-square bg-light text-dark flex-shrink-0 me-3">
129 <img class="d-block mx-auto mb-4" src="/static/img/logo-k2.png" alt="" width="60"> 142 <img class="d-block mx-auto mb-4" src="/static/img/logo-k2.png" alt="" width="60">
130 </div> 143 </div>
131 <div> 144 <div>
132 <h2>{{ component.name|title }}</h2> 145 <h2>{{ component.name|title }}</h2>
133 <p>{{ component.description }}</p> 146 <p>{{ component.description }}</p>
134 <p>ВЕРСІЯ {{ component.latest_version }}</p> 147 <p>ВЕРСІЯ {{ component.latest_version }}</p>
135 {% if component.name in components_names %} 148 {% if component.name in components_names %}
136 <button type="button" class="btn btn-success">Installed</button> 149 <button type="button" class="btn btn-success">Installed</button>
137 {% else %} 150 {% else %}
138 <a href="/show_components/{{ component.id }}" class="btn btn-primary">Install</a> 151 <a href="/show_components/{{ component.id }}" class="btn btn-primary">Install</a>
139 {% endif %} 152 {% endif %}
140 </div> 153 </div>
141 </div> 154 </div>
142 {% endfor %} 155 {% endfor %}
143 {% else %} 156 {% else %}
144 <div class="col"> 157 <div class="col">
145 <p>No components available.</p> 158 <p>No components available.</p>
146 </div> 159 </div>
147 {% endif %} 160 {% endif %}
148 </div> 161 </div>
149 </div> 162 </div>
150 163
151 {% endblock %} 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