shopyo-appadmin 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,13 @@
1
+ Metadata-Version: 2.1
2
+ Name: shopyo_appadmin
3
+ Version: 1.0.0
4
+ Summary: Base module containing jinja macros and templates
5
+ Author-email: Abdur-Rahmaan Janhangeer <arj.python@gmail.com>
6
+ Project-URL: Homepage, https://github.com/shopyo/shopyo
7
+ Project-URL: Bug Tracker, https://github.com/shopyo/shopyo/issues
8
+ Keywords: shopyo
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: License :: OSI Approved :: BSD License
11
+ Classifier: Operating System :: OS Independent
12
+ Requires-Python: >=3.8
13
+ Requires-Dist: shopyo
@@ -0,0 +1,36 @@
1
+ [project]
2
+ name = "shopyo_appadmin"
3
+ authors = [{ name = "Abdur-Rahmaan Janhangeer", email = "arj.python@gmail.com" }]
4
+ description = "Base module containing jinja macros and templates"
5
+ # readme = "README.md"
6
+ requires-python = ">=3.8"
7
+ # license = { file = "LICENSE.txt" }
8
+ keywords = ["shopyo"]
9
+ classifiers = [
10
+ "Programming Language :: Python :: 3",
11
+ "License :: OSI Approved :: BSD License",
12
+ "Operating System :: OS Independent",
13
+ ]
14
+ dependencies = ["shopyo"]
15
+ dynamic = ["version"]
16
+
17
+ [tool.setuptools.dynamic]
18
+ version = { attr = "shopyo_appadmin.__version__" }
19
+
20
+ [project.urls]
21
+ "Homepage" = "https://github.com/shopyo/shopyo"
22
+ "Bug Tracker" = "https://github.com/shopyo/shopyo/issues"
23
+
24
+ [build-system]
25
+ requires = ["setuptools>=61.0"]
26
+ build-backend = "setuptools.build_meta"
27
+
28
+ [tool.setuptools]
29
+ include-package-data = true
30
+
31
+ [tool.setuptools.packages.find]
32
+ where = ["."]
33
+ exclude = ["tests*", "docs*", "examples*"]
34
+
35
+ [tool.setuptools.package-data]
36
+ shopyo_appadmin = ["static/**", "templates/**", "*.json"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,20 @@
1
+ from typing import Any
2
+
3
+ from flask import Flask
4
+ from shopyo_appadmin.view import module_blueprint
5
+
6
+ __version__ = "1.0.0"
7
+
8
+
9
+ class ShopyoAppAdmin:
10
+ def __init__(self, app: Any = None) -> None:
11
+ if app is not None:
12
+ self.init_app(app)
13
+
14
+ def init_app(self, app: Flask) -> None:
15
+ if not hasattr(app, "extensions"):
16
+ app.extensions = {}
17
+
18
+ app.extensions["shopyo_appadmin"] = self
19
+ bp = module_blueprint
20
+ app.register_blueprint(bp)
@@ -0,0 +1,26 @@
1
+ from functools import wraps
2
+
3
+ from flask import redirect
4
+ from flask_login import current_user
5
+ from init import login_manager
6
+ from shopyo_auth.models import User
7
+
8
+ from shopyo.api.html import notify_warning
9
+
10
+ login_manager.login_message = notify_warning("Please login for access")
11
+
12
+
13
+ @login_manager.user_loader
14
+ def load_user(id):
15
+ return User.query.get(id)
16
+
17
+
18
+ def admin_required(f):
19
+ @wraps(f)
20
+ def wrap(*args, **kwargs):
21
+ if current_user.is_admin:
22
+ return f(*args, **kwargs)
23
+ else:
24
+ return redirect("/")
25
+
26
+ return wrap
@@ -0,0 +1,12 @@
1
+ {
2
+ "display_string": "Admin",
3
+ "type": "show",
4
+ "fa-icon": "fas fa-user-lock",
5
+ "url_prefix": "/appadmin",
6
+ "module_name": "appadmin",
7
+ "author": {
8
+ "name":"Abdur-Rahmaan Janhangeer",
9
+ "website":"https://www.pythonkitchen.com/about-me/",
10
+ "mail":"arj.python@gmail.com"
11
+ }
12
+ }
File without changes
@@ -0,0 +1,86 @@
1
+ {% extends "shopyo_base/module_base.html" %}
2
+ {% set active_page = "admin" %}
3
+ {% block pagehead %}
4
+ <title>{{active_page.capitalize()}}</title>
5
+ <style>
6
+ .hidden {
7
+ display: none;
8
+ }
9
+
10
+ .show {
11
+ display: inline-block;
12
+ }
13
+
14
+ .active {
15
+ width: 6rem;
16
+ color: white;
17
+ background-color: #34ce57;
18
+ }
19
+
20
+ .inactive {
21
+ width: 6rem;
22
+ color: white;
23
+ background-color: #ff253a;
24
+ }
25
+
26
+ .all_inactive {
27
+ display: none;
28
+ }
29
+ </style>
30
+ {% endblock %}
31
+ {% block sidebar %}
32
+ {%include 'appadmin/blocks/sidebar.html'%}
33
+ {%endblock%}
34
+ {% block content %}
35
+ <br>
36
+ <div class="container col-md-6 off-set-3">
37
+ <div class="card">
38
+ <div class="card-body">
39
+ <form action="/appadmin/add" method="post">
40
+ <div class="input-group mb-3">
41
+ <div class="input-group-prepend">
42
+ <span class="input-group-text"><i class="fa fa-user"></i></span>
43
+ </div>
44
+ <input type="email" name="email" class="form-control" id="email" placeholder="Email" required autocomplete="off">
45
+ </div>
46
+ <div class="input-group mb-3">
47
+ <div class="input-group-prepend">
48
+ <span class="input-group-text"><i class="fa fa-key"></i></span>
49
+ </div>
50
+ <input type="password" name="password" class="form-control" id="user_password" placeholder="Password" required autocomplete="off">
51
+ </div>
52
+ <div class="input-group mb-3">
53
+ <div class="input-group-prepend">
54
+ <span class="input-group-text"><i class="fa fa-key"></i></span>
55
+ </div>
56
+ <input type="text" name="first_name" class="form-control" placeholder="First Name" autocomplete="off">
57
+ </div>
58
+ <div class="input-group mb-3">
59
+ <div class="input-group-prepend">
60
+ <span class="input-group-text"><i class="fa fa-key"></i></span>
61
+ </div>
62
+ <input type="text" name="last_name" class="form-control" placeholder="Last Name" autocomplete="off">
63
+ </div>
64
+ <div class="input-group mb-3">
65
+ <div class="custom-control custom-checkbox">
66
+ <input type="checkbox" name="is_admin" class="custom-control-input" id="is_admin" value="True" checked>
67
+ <label for="is_admin" class="custom-control-label">Admin User</label>
68
+ </div>
69
+ </div>
70
+ <table class="table">
71
+ <tbody>
72
+ {%for role in roles%}
73
+ <tr>
74
+ <td><input type="checkbox" name="role_{{role.id}}"></td>
75
+ <td>{{role.name}}</td>
76
+ </tr>
77
+ {%endfor%}
78
+ </tbody>
79
+ </table>
80
+ <input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
81
+ <input type="submit" class="btn btn-info" value="add" required>
82
+ </form>
83
+ </div>
84
+ </div>
85
+ </div>
86
+ {% endblock %}
@@ -0,0 +1,25 @@
1
+ {{
2
+ sidebar_item(
3
+ 'Home',
4
+ icon='fas fa-user-lock',
5
+ url=url_for('appadmin.user_list')
6
+ )
7
+ }}
8
+
9
+
10
+ {{
11
+ sidebar_item(
12
+ 'Add',
13
+ icon='fas fa-plus-circle',
14
+ url=url_for('appadmin.user_add')
15
+ )
16
+ }}
17
+
18
+
19
+ {{
20
+ sidebar_item(
21
+ 'Roles',
22
+ icon='fa fa-shield-alt',
23
+ url=url_for('appadmin.roles')
24
+ )
25
+ }}
@@ -0,0 +1,69 @@
1
+ {% extends "shopyo_base/module_base.html" %}
2
+ {% set active_page = "admin" %}
3
+ {% block pagehead %}
4
+ <title>edit</title>
5
+ {% endblock %}
6
+ {% block sidebar %}
7
+ {%include 'appadmin/blocks/sidebar.html'%}
8
+ {%endblock%}
9
+ {% block content %}
10
+ <br>
11
+ <div class="card">
12
+ <div class="card-body">
13
+ <h2>Edit User</h2>
14
+ <form action='/appadmin/update' method="post">
15
+ <label>Edit User:<span> {{ user.email }} </span></label>
16
+ <div class="input-group mb-3">
17
+ </div>
18
+ <div class="input-group mb-3">
19
+ <div class="input-group-prepend">
20
+ <span class="input-group-text"><i class="fa fa-key"></i></span>
21
+ </div>
22
+ <input type="text" name="email" class="form-control" id="user_email" value="{{ user.email }}" required autocomplete="off">
23
+ </div>
24
+ <div class="input-group mb-3">
25
+ <div class="input-group-prepend">
26
+ <span class="input-group-text"><i class="fa fa-key"></i></span>
27
+ </div>
28
+ <input type="password" name="password" class="form-control" id="user_password" value="" autocomplete="off">
29
+ </div>
30
+ <div class="">
31
+ <div class="">
32
+ <input type="checkbox" name="is_admin" id="is_admin" class="" {{'checked' if user.is_admin}}>
33
+ <label for="is_admin" class="custom-control-label">Admin User</label>
34
+ </div>
35
+ </div>
36
+ <div class="input-group mb-3">
37
+ <div class="input-group-prepend">
38
+ <span class="input-group-text"><i class="fa fa-key"></i></span>
39
+ </div>
40
+ <input type="text" name="first_name" class="form-control" placeholder="First Name" autocomplete="off" value="{{ user.first_name }}">
41
+ </div>
42
+ <div class="input-group mb-3">
43
+ <div class="input-group-prepend">
44
+ <span class="input-group-text"><i class="fa fa-key"></i></span>
45
+ </div>
46
+ <input type="text" name="last_name" class="form-control" placeholder="Last Name" autocomplete="off" value="{{ user.last_name }}">
47
+ </div>
48
+ <input required name="id" id="user_id" type="hidden" class="form-control" value="{{ user.id }}">
49
+ <div>
50
+ Roles<br>
51
+ <table class="table">
52
+ <tbody>
53
+ {%for role in roles%}
54
+ <tr>
55
+ <td><input type="checkbox" name="role_{{role.id}}" {%if role.name in user_roles%} checked {%endif%}></td>
56
+ <td>{{role.name}}</td>
57
+ </tr>
58
+ {%endfor%}
59
+ </tbody>
60
+ </table>
61
+ </div>
62
+ <div class="input-group mb-3">
63
+ <input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
64
+ <input required type="submit" class="btn btn-info" value="edit">
65
+ </div>
66
+ </form>
67
+ </div>
68
+ </div>
69
+ {% endblock %}
@@ -0,0 +1,81 @@
1
+ {% extends "shopyo_base/module_base.html" %}
2
+ {% set active_page ='admin' %}
3
+ {% block pagehead %}
4
+ <title>{{active_page.capitalize()}}</title>
5
+ <style>
6
+ .hidden{
7
+ display: none;
8
+ }
9
+ .show{
10
+ display: inline-block;
11
+ }
12
+ </style>
13
+ {% endblock %}
14
+ {% block sidebar %}
15
+ {%include 'appadmin/blocks/sidebar.html'%}
16
+ {%endblock%}
17
+ {% block content %}
18
+ <br>
19
+ <script type="text/javascript">
20
+ $(function() {
21
+ $('.delete_me').click(function() {
22
+ var id = $(this).data('id');
23
+ console.log(id);
24
+ var ask = confirm("Do you want to delete ?");
25
+ if (ask == true) {
26
+ window.open("/appadmin/delete/" + id, "_self")
27
+ } else {
28
+
29
+ }
30
+ });
31
+ });
32
+ </script>
33
+ <div class="container">
34
+ <div class="card">
35
+ <div class="card-body">
36
+ <h2>{{active_page.capitalize()}}</h2>
37
+ <table class="table table-responsive table-bordered">
38
+ <thead>
39
+ <tr>
40
+ <th>ID</th>
41
+ <th>Email</th>
42
+ <th>Password</th>
43
+ <th>Admin</th>
44
+ <th>Roles</th>
45
+ <th style="border: none;"></th>
46
+ </tr>
47
+ </thead>
48
+ <tbody>
49
+ {% for user in users %}
50
+ <tr>
51
+ <td>{{user.id}}</td>
52
+ <td>{{user.email}}</td>
53
+ <td>*******</td>
54
+ <td>{{user.is_admin}}</td>
55
+ <td>
56
+ {%for role in user.roles%}
57
+ <div class="badge badge-primary">
58
+ {{role.name}}
59
+ </div>
60
+ {%endfor%}
61
+ </td>
62
+ <td style="border: none;">
63
+ <div style='display: inline-block; '>
64
+ <a style="text-decoration: none;">
65
+ <button id='delete' data-id="{{ user.id }}" class="delete_me btn btn-dark" type="" name="{{ user.id }}"><i class="fas fa-trash-alt"></i></button>
66
+ </a>
67
+ &nbsp;
68
+ <a href="/appadmin/edit/{{ user.id }}" style="text-decoration: none;">
69
+ <button data-name="{{ user.id }}" class="btn btn-warning"><i id="ico-{{user.id}}" class="fas fa-pencil-alt"></i>
70
+ </button>
71
+ </a>
72
+ </div>
73
+ </td>
74
+ </tr>
75
+ {% endfor %}
76
+ </tbody>
77
+ </table>
78
+ </div>
79
+ </div>
80
+ </div>
81
+ {% endblock %}
@@ -0,0 +1,40 @@
1
+ {% extends "shopyo_base/module_base.html" %}
2
+ {% set active_page = "admin" %}
3
+ {% block pagehead %}
4
+ {% endblock %}
5
+ {% block sidebar %}
6
+ {%include 'appadmin/blocks/sidebar.html'%}
7
+ {%endblock%}
8
+ {% block content %}
9
+ <br>
10
+ <div class="card">
11
+ <div class="card-body">
12
+ <h2>Roles</h2>
13
+ <div class="">
14
+ <form class="" method="POST" action='{{url_for("appadmin.roles_add")}}'>
15
+ <div class="form-group">
16
+ <input name="csrf_token" value="{{csrf_token()}}" type="hidden">
17
+ <input class="form-control" type="" name="name"><br>
18
+ <button class="btn btn-info" type="submit">add</button>
19
+ </div>
20
+ </form>
21
+ <table>
22
+ <tbody>
23
+ {%for role in roles%}
24
+ <tr>
25
+ <form action="{{url_for('appadmin.roles_update')}}" method="POST">
26
+ <td>
27
+ <input value="{{role.name}}" class="form-control" type="" name="role_name"></td>
28
+ <input type="hidden" name="role_id" value="{{role.id}}">
29
+ <input type="hidden" name="csrf_token" value="{{csrf_token()}}">
30
+ <td><button class="btn btn-info form-control" type="submit">save</button></td>
31
+ <td><a class="btn btn-danger" href="{{url_for('appadmin.roles_delete', role_id=role.id)}}">delete</a></td>
32
+ </form>
33
+ </tr>
34
+ {%endfor%}
35
+ </tbody>
36
+ </table>
37
+ </div>
38
+ </div>
39
+ </div>
40
+ {% endblock %}
@@ -0,0 +1,236 @@
1
+ """
2
+ .. module:: AdminViews
3
+ :synopsis: All endpoints of the admin views are defined here.
4
+
5
+ """
6
+
7
+ from flask import flash
8
+ from flask import redirect
9
+ from flask import render_template
10
+ from flask import request
11
+ from flask import url_for
12
+ from flask_login import login_required
13
+ from init import db
14
+ from shopyo_auth.models import Role
15
+ from shopyo_auth.models import User
16
+ from sqlalchemy import exists
17
+
18
+ from .admin import admin_required
19
+ from shopyo.api.html import notify_success
20
+ from shopyo.api.html import notify_warning
21
+ from shopyo.api.module import ModuleHelp
22
+
23
+ # from config import Config
24
+
25
+ mhelp = ModuleHelp(__file__, __name__)
26
+ globals()[mhelp.blueprint_str] = mhelp.blueprint
27
+ module_blueprint = globals()[mhelp.blueprint_str]
28
+
29
+
30
+ @module_blueprint.route("/")
31
+ @login_required
32
+ @admin_required
33
+ def user_list():
34
+ """
35
+ **Get The List of User**
36
+
37
+ Lists all users in the database.
38
+
39
+ """
40
+ context = {}
41
+ context["users"] = User.query.all()
42
+ return render_template("appadmin/index.html", **context)
43
+
44
+
45
+ @module_blueprint.route("/add", methods=["GET", "POST"])
46
+ @login_required
47
+ @admin_required
48
+ def user_add():
49
+ """
50
+ **Adds a User**
51
+
52
+ adds a user to database.
53
+
54
+ """
55
+ context = {}
56
+ if request.method == "POST":
57
+ email = request.form["email"]
58
+ password = request.form["password"]
59
+ first_name = request.form["first_name"]
60
+ last_name = request.form["last_name"]
61
+ admin_user = request.form.get("is_admin")
62
+ if admin_user == "True":
63
+ is_admin = True
64
+ else:
65
+ is_admin = False
66
+
67
+ has_user = db.session.query(exists().where(User.email == email)).scalar()
68
+
69
+ if not has_user:
70
+ new_user = User()
71
+ new_user.email = email
72
+ new_user.is_admin = is_admin
73
+ new_user.first_name = first_name
74
+ new_user.last_name = last_name
75
+ new_user.password = password
76
+
77
+ for key in request.form:
78
+ if key.startswith("role_"):
79
+ role_id = key.split("_")[1]
80
+ role = Role.get_by_id(role_id)
81
+ new_user.roles.append(role)
82
+ new_user.save()
83
+ return redirect(url_for("appadmin.user_add"))
84
+
85
+ flash(notify_warning("User with same email already exists"))
86
+
87
+ context["roles"] = Role.query.all()
88
+ return render_template("appadmin/add.html", **context)
89
+
90
+
91
+ @module_blueprint.route("/delete/<id>", methods=["GET"])
92
+ @login_required
93
+ @admin_required
94
+ def admin_delete(id):
95
+ """
96
+ **Delete a User**
97
+
98
+ :param id: id of the user
99
+ :type id: int
100
+
101
+ """
102
+ user = User.query.get(id)
103
+
104
+ if user is None:
105
+ flash(notify_warning("Unable to delete. Invalid user id"))
106
+ return redirect("/appadmin")
107
+
108
+ user.delete()
109
+ flash(notify_success("User successfully deleted"))
110
+ return redirect("/appadmin")
111
+
112
+
113
+ @module_blueprint.route("/edit/<id>", methods=["GET"])
114
+ @login_required
115
+ @admin_required
116
+ def admin_edit(id):
117
+ """
118
+ **Update information for a User**
119
+
120
+ :param id: id of the user
121
+ :type id: int
122
+
123
+ """
124
+ context = {}
125
+ user = User.query.get(id)
126
+
127
+ if user is None:
128
+ flash(notify_warning("Unable to edit. Invalid user id"))
129
+ return redirect("/appadmin")
130
+
131
+ context["user"] = user
132
+ context["user_roles"] = [r.name for r in user.roles]
133
+ context["roles"] = Role.query.all()
134
+ return render_template("appadmin/edit.html", **context)
135
+
136
+
137
+ @module_blueprint.route("/update", methods=["POST"])
138
+ @login_required
139
+ @admin_required
140
+ def admin_update():
141
+ """
142
+ **Update a User record**
143
+
144
+ """
145
+ id = request.form["id"]
146
+ password = request.form["password"]
147
+ email = request.form["email"]
148
+ first_name = request.form["first_name"]
149
+ last_name = request.form["last_name"]
150
+ is_admin = request.form.get("is_admin")
151
+
152
+ if is_admin:
153
+ is_admin = True
154
+ else:
155
+ is_admin = False
156
+
157
+ user = User.query.get(id)
158
+
159
+ if user is None:
160
+ flash(notify_warning("Unable to update. User does not exist."))
161
+ return redirect("/admin")
162
+
163
+ user.is_admin = is_admin
164
+ user.email = email
165
+ user.first_name = first_name
166
+ user.last_name = last_name
167
+ user.roles[:] = []
168
+
169
+ if password.strip():
170
+ user.password = password
171
+
172
+ for key in request.form:
173
+ if key.startswith("role_"):
174
+ role_id = key.split("_")[1]
175
+ role = Role.get_by_id(role_id)
176
+ user.roles.append(role)
177
+
178
+ user.update()
179
+ flash(notify_success("User successfully updated"))
180
+ return redirect("/appadmin")
181
+
182
+
183
+ @module_blueprint.route("/roles")
184
+ @login_required
185
+ @admin_required
186
+ def roles():
187
+ context = {}
188
+ context["roles"] = Role.query.all()
189
+ return render_template("appadmin/roles.html", **context)
190
+
191
+
192
+ @module_blueprint.route("/roles/add", methods=["POST"])
193
+ @login_required
194
+ @admin_required
195
+ def roles_add():
196
+ if request.method == "POST":
197
+ if not Role.query.filter(Role.name == request.form["name"]).first():
198
+ role = Role(name=request.form["name"])
199
+ role.save()
200
+ flash(notify_success("Role successfully added"))
201
+ return redirect(url_for("appadmin.roles"))
202
+ flash(notify_warning("Role already exists"))
203
+ return redirect(url_for("appadmin.roles"))
204
+
205
+
206
+ @module_blueprint.route("/roles/<role_id>/delete", methods=["GET"])
207
+ @login_required
208
+ @admin_required
209
+ def roles_delete(role_id):
210
+ role = Role.get_by_id(role_id)
211
+
212
+ if role is None:
213
+ flash(notify_warning("Unable to delete. Invalid role id"))
214
+ return redirect(url_for("appadmin.roles"))
215
+
216
+ role.delete()
217
+ flash(notify_success("Role successfully deleted"))
218
+ return redirect(url_for("appadmin.roles"))
219
+
220
+
221
+ @module_blueprint.route("/roles/update", methods=["POST"])
222
+ @login_required
223
+ @admin_required
224
+ def roles_update():
225
+ if request.method == "POST":
226
+ role = Role.get_by_id(request.form["role_id"])
227
+
228
+ if role is None:
229
+ flash(notify_warning("Unable to update. Role does not exist"))
230
+ return redirect(url_for("appadmin.roles"))
231
+
232
+ role.name = request.form["role_name"]
233
+ role.update()
234
+ flash(notify_success("Role successfully updated"))
235
+
236
+ return redirect(url_for("appadmin.roles"))
@@ -0,0 +1,13 @@
1
+ Metadata-Version: 2.1
2
+ Name: shopyo_appadmin
3
+ Version: 1.0.0
4
+ Summary: Base module containing jinja macros and templates
5
+ Author-email: Abdur-Rahmaan Janhangeer <arj.python@gmail.com>
6
+ Project-URL: Homepage, https://github.com/shopyo/shopyo
7
+ Project-URL: Bug Tracker, https://github.com/shopyo/shopyo/issues
8
+ Keywords: shopyo
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: License :: OSI Approved :: BSD License
11
+ Classifier: Operating System :: OS Independent
12
+ Requires-Python: >=3.8
13
+ Requires-Dist: shopyo
@@ -0,0 +1,18 @@
1
+ pyproject.toml
2
+ shopyo_appadmin/__init__.py
3
+ shopyo_appadmin/admin.py
4
+ shopyo_appadmin/info.json
5
+ shopyo_appadmin/models.py
6
+ shopyo_appadmin/view.py
7
+ shopyo_appadmin.egg-info/PKG-INFO
8
+ shopyo_appadmin.egg-info/SOURCES.txt
9
+ shopyo_appadmin.egg-info/dependency_links.txt
10
+ shopyo_appadmin.egg-info/requires.txt
11
+ shopyo_appadmin.egg-info/top_level.txt
12
+ shopyo_appadmin/templates/shopyo_appadmin/add.html
13
+ shopyo_appadmin/templates/shopyo_appadmin/edit.html
14
+ shopyo_appadmin/templates/shopyo_appadmin/index.html
15
+ shopyo_appadmin/templates/shopyo_appadmin/roles.html
16
+ shopyo_appadmin/templates/shopyo_appadmin/blocks/sidebar.html
17
+ tests/test_admin.py
18
+ tests/test_models.py
@@ -0,0 +1,2 @@
1
+ dist
2
+ shopyo_appadmin
@@ -0,0 +1,406 @@
1
+ """
2
+ This file (test_admin.py) contains the functional tests for
3
+ the `admin` blueprint.
4
+
5
+ These tests use GETs and POSTs to different endpoints to check
6
+ for the proper behavior of the `admin` blueprint.
7
+ """
8
+
9
+ import json
10
+ import os
11
+
12
+ import pytest
13
+ from flask import request
14
+ from flask import url_for
15
+ from modules.box__default.auth.models import Role
16
+ from modules.box__default.auth.models import role_user_bridge
17
+ from modules.box__default.auth.models import User
18
+
19
+
20
+ dirpath = os.path.dirname(os.path.abspath(__file__))
21
+ module_path = os.path.dirname(dirpath)
22
+
23
+ module_info = None
24
+
25
+
26
+ # class TestAdminInvalidAccess:
27
+ # """
28
+ # Test all admin routes for correct user authentication
29
+ # """
30
+
31
+ # routes_get = [
32
+ # "/",
33
+ # "/add",
34
+ # "/delete/<id>",
35
+ # "/edit/<id>",
36
+ # "/roles",
37
+ # "/roles/<role_id>/delete",
38
+ # ]
39
+
40
+ # routes_post = ["/update", "/roles/update", "/roles/add", "/add"]
41
+
42
+ # @pytest.mark.parametrize("route", routes_get)
43
+ # def test_redirect_if_not_logged_in_get(self, test_client, route, auth):
44
+ # auth.logout()
45
+ # with open(os.path.join(module_path, "info.json")) as f:
46
+ # module_info = json.load(f)
47
+ # response = test_client.get(
48
+ # f"{module_info['url_prefix']}{route}", follow_redirects=True
49
+ # )
50
+
51
+ # assert response.status_code == 200
52
+ # assert request.path == url_for("shopyo_auth.login")
53
+
54
+ # @pytest.mark.parametrize("route", routes_post)
55
+ # def test_redirect_if_not_logged_in_post(self, test_client, route, auth):
56
+ # auth.logout()
57
+ # with open(os.path.join(module_path, "info.json")) as f:
58
+ # module_info = json.load(f)
59
+ # response = test_client.post(
60
+ # f"{module_info['url_prefix']}{route}", follow_redirects=True
61
+ # )
62
+
63
+ # assert response.status_code == 200
64
+ # assert request.path == url_for("shopyo_auth.login")
65
+
66
+ # @pytest.mark.usefixtures("login_non_admin_user")
67
+ # @pytest.mark.parametrize("route", routes_get)
68
+ # def test_no_admin_access_if_not_admin_get(self, test_client, route):
69
+ # with open(os.path.join(module_path, "info.json")) as f:
70
+ # module_info = json.load(f)
71
+ # response = test_client.get(
72
+ # f"{module_info['url_prefix']}{route}", follow_redirects=True
73
+ # )
74
+
75
+ # assert response.status_code == 200
76
+ # assert request.path == url_for("shopyo_dashboard.index")
77
+ # assert b"You need to be an admin to view this page" in response.data
78
+
79
+ # @pytest.mark.usefixtures("login_non_admin_user")
80
+ # @pytest.mark.parametrize("route", routes_post)
81
+ # def test_no_admin_access_if_not_admin_post(self, test_client, route):
82
+ # with open(os.path.join(module_path, "info.json")) as f:
83
+ # module_info = json.load(f)
84
+ # response = test_client.post(
85
+ # f"{module_info['url_prefix']}{route}", follow_redirects=True
86
+ # )
87
+
88
+ # assert response.status_code == 200
89
+ # assert request.path == url_for("shopyo_dashboard.index")
90
+ # assert b"You need to be an admin to view this page" in response.data
91
+
92
+
93
+ @pytest.mark.usefixtures("login_admin_user")
94
+ class TestAdminEndpoints:
95
+ """
96
+ Test all admin api post and get requests
97
+ """
98
+
99
+ # def test_admin_user_list_get(self, test_client):
100
+ # with open(os.path.join(module_path, "info.json")) as f:
101
+ # module_info = json.load(f)
102
+ # response = test_client.get(f"{module_info['url_prefix']}/")
103
+
104
+ # assert response.status_code == 200
105
+ # assert b"Admin" in response.data
106
+ # assert b"id" in response.data
107
+ # assert b"Email" in response.data
108
+ # assert b"Password" in response.data
109
+ # assert b"Roles" in response.data
110
+
111
+ # def test_admin_add_get(self, test_client):
112
+ # with open(os.path.join(module_path, "info.json")) as f:
113
+ # module_info = json.load(f)
114
+ # response = test_client.get(f"{module_info['url_prefix']}/add")
115
+
116
+ # assert response.status_code == 200
117
+ # assert b"Email" in response.data
118
+ # assert b"Password" in response.data
119
+ # assert b"First Name" in response.data
120
+ # assert b"Last Name" in response.data
121
+ # assert b"Admin User" in response.data
122
+
123
+ # def test_admin_add_unique_user_post(self, test_client):
124
+ # with open(os.path.join(module_path, "info.json")) as f:
125
+ # module_info = json.load(f)
126
+ # role1 = Role.create(name="test1-role")
127
+ # role2 = Role.create(name="test2-role")
128
+ # data = {
129
+ # "email": "test@gmail.com",
130
+ # "password": "pass",
131
+ # "first_name": "Test",
132
+ # "last_name": "User",
133
+ # "is_admin": "",
134
+ # f"role_{role1.id}": "",
135
+ # f"role_{role2.id}": "",
136
+ # }
137
+
138
+ # test_client.post(
139
+ # f"{module_info['url_prefix']}/add",
140
+ # data=data,
141
+ # follow_redirects=True,
142
+ # )
143
+ # test_user = User.query.filter(User.email == "test@gmail.com").scalar()
144
+
145
+ # assert test_user is not None
146
+ # assert test_user.first_name == "Test"
147
+ # assert test_user.last_name == "User"
148
+ # assert test_user.is_admin is False
149
+ # assert test_user.roles is not None
150
+ # assert len(test_user.roles) == 2
151
+ # assert role1.users[0].email == "test@gmail.com"
152
+ # assert role2.users[0].email == "test@gmail.com"
153
+
154
+ # def test_admin_add_existing_user_post(self, test_client):
155
+ # with open(os.path.join(module_path, "info.json")) as f:
156
+ # module_info = json.load(f)
157
+ # User.create(email="test@gmail.com", password="pass")
158
+ # data = {
159
+ # "email": "test@gmail.com",
160
+ # "password": "pass",
161
+ # "first_name": "Test",
162
+ # "last_name": "User",
163
+ # "is_admin": "",
164
+ # }
165
+
166
+ # response = test_client.post(
167
+ # f"{module_info['url_prefix']}/add",
168
+ # data=data,
169
+ # follow_redirects=True,
170
+ # )
171
+ # test_users = User.query.filter(User.email == "test@gmail.com").count()
172
+
173
+ # assert response.status_code == 200
174
+ # assert b"User with same email already exists" in response.data
175
+ # assert test_users == 1
176
+
177
+ # def test_admin_delete_existing_user_get(self, test_client):
178
+ # with open(os.path.join(module_path, "info.json")) as f:
179
+ # module_info = json.load(f)
180
+ # user = User(email="test@gmail.com", password="pass")
181
+ # role1 = Role(name="test1-role")
182
+ # role2 = Role(name="test2-role")
183
+ # user.roles = [role1, role2]
184
+ # user.save()
185
+
186
+ # response = test_client.get(
187
+ # f"{module_info['url_prefix']}/delete/{user.id}",
188
+ # follow_redirects=True,
189
+ # )
190
+ # test_user = User.query.filter(User.email == user.email).scalar()
191
+ # test_roles = Role.query.count()
192
+ # user_role = (
193
+ # User.query.join(role_user_bridge)
194
+ # .join(Role)
195
+ # .filter(User.id == user.id)
196
+ # .scalar()
197
+ # )
198
+
199
+ # assert response.status_code == 200
200
+ # assert test_user is None
201
+ # assert user_role is None
202
+ # assert test_roles == 2
203
+
204
+ # def test_admin_delete_nonexisting_user_get(self, test_client):
205
+ # with open(os.path.join(module_path, "info.json")) as f:
206
+ # module_info = json.load(f)
207
+ # response = test_client.get(
208
+ # f"{module_info['url_prefix']}/delete/some_id",
209
+ # follow_redirects=True,
210
+ # )
211
+
212
+ # assert response.status_code == 200
213
+ # assert b"Unable to delete. Invalid user id" in response.data
214
+
215
+ # def test_admin_edit_existing_user_get(self, test_client):
216
+ # with open(os.path.join(module_path, "info.json")) as f:
217
+ # module_info = json.load(f)
218
+ # user = User.create(email="test@gmail.com", password="pass")
219
+
220
+ # response = test_client.get(
221
+ # f"{module_info['url_prefix']}/edit/{user.id}",
222
+ # )
223
+
224
+ # assert response.status_code == 200
225
+ # assert b"test@gmail.com" in response.data
226
+ # assert b"Edit User" in response.data
227
+
228
+ # def test_admin_edit_nonexisting_user_get(self, test_client):
229
+ # with open(os.path.join(module_path, "info.json")) as f:
230
+ # module_info = json.load(f)
231
+ # response = test_client.get(
232
+ # f"{module_info['url_prefix']}/edit/some-id", follow_redirects=True
233
+ # )
234
+
235
+ # assert response.status_code == 200
236
+ # assert b"Invalid user id" in response.data
237
+ # assert request.path == f"{module_info['url_prefix']}/"
238
+
239
+ # def test_admin_update_user_adding_new_roles_to_user(self, test_client):
240
+ # with open(os.path.join(module_path, "info.json")) as f:
241
+ # module_info = json.load(f)
242
+ # user = User.create(email="foo@gmail.com", password="pass")
243
+ # role1 = Role.create(name="test1-role")
244
+ # role2 = Role.create(name="test2-role")
245
+ # data = {
246
+ # "id": str(user.id),
247
+ # "email": "bar@gmail.com",
248
+ # "password": "newpass",
249
+ # "first_name": "Test",
250
+ # "last_name": "User",
251
+ # "is_admin": "",
252
+ # f"role_{role1.id}": "",
253
+ # f"role_{role2.id}": "",
254
+ # }
255
+
256
+ # response = test_client.post(
257
+ # f"{module_info['url_prefix']}/update",
258
+ # data=data,
259
+ # follow_redirects=True,
260
+ # )
261
+
262
+ # assert response.status_code == 200
263
+ # assert user.email == "bar@gmail.com"
264
+ # assert user.check_password("newpass")
265
+ # assert user.first_name == "Test"
266
+ # assert user.last_name == "User"
267
+ # assert len(user.roles) == 2
268
+ # assert role1.users[0].email == "bar@gmail.com"
269
+ # assert role2.users[0].email == "bar@gmail.com"
270
+
271
+ # def test_admin_update_user_remove_old_roles_from_user(self, test_client):
272
+ # with open(os.path.join(module_path, "info.json")) as f:
273
+ # module_info = json.load(f)
274
+ # user = User(email="foo@gmail.com", password="pass", is_admin=True)
275
+ # user.is_admin = True
276
+ # role1 = Role(name="test1-role")
277
+ # role2 = Role(name="test2-role")
278
+ # user.roles = [role1, role2]
279
+ # user.save()
280
+ # data = {
281
+ # "id": str(user.id),
282
+ # "email": "bar@gmail.com",
283
+ # "first_name": "Test",
284
+ # "last_name": "User",
285
+ # "password": " ",
286
+ # "is_admin": None,
287
+ # }
288
+
289
+ # response = test_client.post(
290
+ # f"{module_info['url_prefix']}/update",
291
+ # data=data,
292
+ # follow_redirects=True,
293
+ # )
294
+
295
+ # assert response.status_code == 200
296
+ # assert user.email == "bar@gmail.com"
297
+ # assert user.check_password("pass")
298
+ # assert len(user.roles) == 0
299
+ # assert len(role1.users) == 0
300
+ # assert len(role2.users) == 0
301
+
302
+ # def test_admin_roles_get(self, test_client):
303
+ # with open(os.path.join(module_path, "info.json")) as f:
304
+ # module_info = json.load(f)
305
+ # response = test_client.get(f"{module_info['url_prefix']}/roles")
306
+
307
+ # assert response.status_code == 200
308
+ # assert b"Roles" in response.data
309
+
310
+ # def test_admin_roles_add_nonexisting_role_post(self, test_client):
311
+ # with open(os.path.join(module_path, "info.json")) as f:
312
+ # module_info = json.load(f)
313
+ # response = test_client.post(
314
+ # f"{module_info['url_prefix']}/roles/add",
315
+ # data=dict(name="new-role"),
316
+ # follow_redirects=True,
317
+ # )
318
+
319
+ # role = Role.query.filter(Role.name == "new-role").scalar()
320
+ # role_count = Role.query.count()
321
+
322
+ # assert response.status_code == 200
323
+ # assert role is not None
324
+ # assert role_count == 1
325
+
326
+ # def test_admin_roles_add_existing_role_post(self, test_client):
327
+ # with open(os.path.join(module_path, "info.json")) as f:
328
+ # module_info = json.load(f)
329
+ # Role.create(name="new-role")
330
+
331
+ # response = test_client.post(
332
+ # f"{module_info['url_prefix']}/roles/add",
333
+ # data=dict(name="new-role"),
334
+ # follow_redirects=True,
335
+ # )
336
+ # role_count = Role.query.count()
337
+ # role = Role.query.filter(Role.name == "new-role").scalar()
338
+
339
+ # assert response.status_code == 200
340
+ # assert b"Role already exists" in response.data
341
+ # assert role is not None
342
+ # assert role_count == 1
343
+
344
+ # def test_admin_roles_delete_nonexisting_role_get(self, test_client):
345
+ # with open(os.path.join(module_path, "info.json")) as f:
346
+ # module_info = json.load(f)
347
+ # response = test_client.get(
348
+ # f"{module_info['url_prefix']}/roles/some-id/delete",
349
+ # follow_redirects=True,
350
+ # )
351
+
352
+ # assert response.status_code == 200
353
+ # assert request.path == f"{module_info['url_prefix']}/roles"
354
+ # assert b"Unable to delete. Invalid role id" in response.data
355
+
356
+ # def test_admin_roles_delete_existing_role_get(self, test_client):
357
+ # with open(os.path.join(module_path, "info.json")) as f:
358
+ # module_info = json.load(f)
359
+ # role1 = Role.create(name="new-role1")
360
+ # role2 = Role.create(name="new-role2")
361
+
362
+ # response = test_client.get(
363
+ # f"{module_info['url_prefix']}/roles/{role1.id}/delete",
364
+ # follow_redirects=True,
365
+ # )
366
+ # roles = Role.query.all()
367
+
368
+ # assert response.status_code == 200
369
+ # assert request.path == f"{module_info['url_prefix']}/roles"
370
+ # assert b"Role successfully deleted" in response.data
371
+ # assert roles is not None
372
+ # assert roles[0].name == role2.name
373
+ # assert len(roles) == 1
374
+
375
+ # def test_admin_roles_update_nonexisting_role_post(self, test_client):
376
+ # with open(os.path.join(module_path, "info.json")) as f:
377
+ # module_info = json.load(f)
378
+ # response = test_client.post(
379
+ # f"{module_info['url_prefix']}/roles/update",
380
+ # data=dict(role_id="some-id"),
381
+ # follow_redirects=True,
382
+ # )
383
+ # roles = Role.query.count()
384
+
385
+ # assert response.status_code == 200
386
+ # assert request.path == f"{module_info['url_prefix']}/roles"
387
+ # assert b"Unable to update. Role does not exist" in response.data
388
+ # assert roles == 0
389
+
390
+ # def test_admin_roles_update_existing_role_post(self, test_client):
391
+ # with open(os.path.join(module_path, "info.json")) as f:
392
+ # module_info = json.load(f)
393
+ # new_role = Role.create(name="new-role1")
394
+
395
+ # response = test_client.post(
396
+ # f"{module_info['url_prefix']}/roles/update",
397
+ # data=dict(role_id=new_role.id, role_name="update-role"),
398
+ # follow_redirects=True,
399
+ # )
400
+ # role = Role.query.scalar()
401
+
402
+ # assert response.status_code == 200
403
+ # assert request.path == f"{module_info['url_prefix']}/roles"
404
+ # assert b"Role successfully updated" in response.data
405
+ # assert role is not None
406
+ # assert role.name == "update-role"
File without changes