Merge branch 'master' of https://github.com/sagidayan/SE-Hub into QA
This commit is contained in:
commit
c9c54dd3f0
10 changed files with 409 additions and 67 deletions
|
@ -22,11 +22,14 @@ from SE_API.Validation_Utils import *
|
|||
from SE_API.Respones_Utils import *
|
||||
from SE_API.Email_Utils import *
|
||||
|
||||
|
||||
|
||||
campus_routes = Blueprint("campus_routes", __name__)
|
||||
auto = Autodoc()
|
||||
|
||||
|
||||
#----------------------------------------------------------
|
||||
# POST
|
||||
#----------------------------------------------------------
|
||||
|
||||
@campus_routes.route('/api/campuses/create/<string:token>', methods=['POST'])
|
||||
@auto.doc()
|
||||
def create_campus(token):
|
||||
|
@ -80,11 +83,20 @@ def create_campus(token):
|
|||
return bad_request()
|
||||
|
||||
send_create_campus_request(user.email, user.name, campus.title)
|
||||
db.put(campus)
|
||||
notify_se_hub_campus_request(campus, campus.title)
|
||||
db.delete(campus)
|
||||
return ok()
|
||||
|
||||
|
||||
|
||||
#----------------------------------------------------------
|
||||
# PUT
|
||||
#----------------------------------------------------------
|
||||
|
||||
#----------------------------------------------------------
|
||||
# GET
|
||||
#----------------------------------------------------------
|
||||
|
||||
@campus_routes.route('/api/campuses/getAll/<string:token>', methods=['GET'])
|
||||
@auto.doc()
|
||||
|
@ -141,6 +153,10 @@ def get_campuses(token):
|
|||
return forbidden("Invalid Token")
|
||||
|
||||
|
||||
#----------------------------------------------------------
|
||||
# DELETE
|
||||
#----------------------------------------------------------
|
||||
|
||||
|
||||
@campus_routes.route('/api/campuses/deleteCampus/<string:token>/<string:campusid>', methods=['DELETE'])
|
||||
@auto.doc()
|
||||
|
@ -191,6 +207,10 @@ def deleteCampus(token,campusid):
|
|||
|
||||
|
||||
|
||||
#----------------------------------------------------------
|
||||
# DOCUMENTATION
|
||||
#----------------------------------------------------------
|
||||
|
||||
@campus_routes.route('/api/campuses/help')
|
||||
def documentation():
|
||||
return auto.html()
|
||||
|
|
|
@ -279,9 +279,6 @@ def getMessagesByCourseName(name):
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@course_routes.route('/api/courses/deleteCourse/<string:token>/<string:courseid>', methods=['DELETE'])
|
||||
@auto.doc()
|
||||
def deleteCourse(token,courseid):
|
||||
|
@ -390,6 +387,11 @@ def deleteCoursesByCampus(token,campusName):
|
|||
return no_content()
|
||||
|
||||
|
||||
|
||||
#----------------------------------------------------------
|
||||
# DOCUMENTATION
|
||||
#----------------------------------------------------------
|
||||
|
||||
@course_routes.route('/api/courses/help')
|
||||
def documentation():
|
||||
return auto.html()
|
|
@ -21,11 +21,13 @@ from models.Project import Project
|
|||
from SE_API.Validation_Utils import *
|
||||
from SE_API.Respones_Utils import *
|
||||
|
||||
|
||||
|
||||
project_routes = Blueprint("project_routes", __name__)
|
||||
auto = Autodoc()
|
||||
|
||||
#----------------------------------------------------------
|
||||
# POST
|
||||
#----------------------------------------------------------
|
||||
|
||||
@project_routes.route('/api/projects/create/<string:token>', methods=['POST'])
|
||||
@auto.doc()
|
||||
def create_project(token):
|
||||
|
@ -67,7 +69,7 @@ def create_project(token):
|
|||
#todo: check legality
|
||||
|
||||
try:
|
||||
project = Project(projectName=payload['projectName'], courseName=payload['courseName'], masterId=user.key().id(), gitRepository=payload['gitRepository'], membersId=[token])
|
||||
project = Project(projectName=payload['projectName'], courseName=payload['courseName'], master_id=user.key().id(), gitRepository=payload['gitRepository'], membersId=[token])
|
||||
except Exception as e:
|
||||
print e
|
||||
return bad_request()
|
||||
|
@ -75,12 +77,17 @@ def create_project(token):
|
|||
db.put(project)
|
||||
db.save
|
||||
return Response(response=project.to_JSON(),
|
||||
status=201,
|
||||
status=200,
|
||||
mimetype="application/json")
|
||||
|
||||
#----------------------------------------------------------
|
||||
# PUT
|
||||
#----------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
#----------------------------------------------------------
|
||||
# GET
|
||||
#----------------------------------------------------------
|
||||
|
||||
@project_routes.route('/api/projects/getProjectsByCourseName/<string:name>', methods=["GET"])
|
||||
@auto.doc()
|
||||
|
@ -131,6 +138,12 @@ def getProjectsByCourseName(name):
|
|||
|
||||
|
||||
|
||||
#----------------------------------------------------------
|
||||
# DELETE
|
||||
#----------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
@project_routes.route('/api/projects/deleteProject/<string:token>/<string:projectid>', methods=['DELETE'])
|
||||
@auto.doc()
|
||||
def deleteProject(token,projectid):
|
||||
|
@ -179,6 +192,11 @@ def deleteProject(token,projectid):
|
|||
|
||||
|
||||
|
||||
|
||||
#----------------------------------------------------------
|
||||
# DOCUMENTATION
|
||||
#----------------------------------------------------------
|
||||
|
||||
@project_routes.route('/api/projects/help')
|
||||
def documentation():
|
||||
return auto.html()
|
|
@ -25,11 +25,15 @@ from SE_API.Validation_Utils import *
|
|||
from SE_API.Respones_Utils import *
|
||||
|
||||
|
||||
|
||||
|
||||
task_routes = Blueprint("task_routes", __name__)
|
||||
auto = Autodoc()
|
||||
|
||||
|
||||
|
||||
#----------------------------------------------------------
|
||||
# POST
|
||||
#----------------------------------------------------------
|
||||
|
||||
@task_routes.route('/api/tasks/create/<string:token>', methods=['POST'])
|
||||
@auto.doc()
|
||||
def create_task(token):
|
||||
|
@ -140,8 +144,13 @@ def create_task(token):
|
|||
return created()
|
||||
|
||||
|
||||
#----------------------------------------------------------
|
||||
# PUT
|
||||
#----------------------------------------------------------
|
||||
|
||||
|
||||
#----------------------------------------------------------
|
||||
# GET
|
||||
#----------------------------------------------------------
|
||||
|
||||
|
||||
@task_routes.route('/api/tasks/getAllTasks/<string:courseName>', methods=["GET"])
|
||||
|
@ -321,6 +330,12 @@ def getTaskComponents(taskId):
|
|||
|
||||
|
||||
|
||||
#----------------------------------------------------------
|
||||
# DELETE
|
||||
#----------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
@task_routes.route('/api/tasks/deleteTask/<string:token>/<string:taskid>', methods=['DELETE'])
|
||||
@auto.doc()
|
||||
def deleteTask(token,taskid):
|
||||
|
@ -422,6 +437,11 @@ def deleteTaskComponents(token,taskid):
|
|||
|
||||
|
||||
|
||||
|
||||
#----------------------------------------------------------
|
||||
# DOCUMENTATION
|
||||
#----------------------------------------------------------
|
||||
|
||||
@task_routes.route('/api/tasks/help')
|
||||
def documentation():
|
||||
return auto.html()
|
||||
|
|
|
@ -16,6 +16,7 @@ from flask.ext.autodoc import Autodoc
|
|||
|
||||
# DB Models
|
||||
from models.User import User
|
||||
from models.Course import Course
|
||||
|
||||
#Validation Utils Libs
|
||||
from SE_API.Validation_Utils import *
|
||||
|
@ -25,55 +26,18 @@ from SE_API.Respones_Utils import *
|
|||
user_routes = Blueprint("user_routes", __name__)
|
||||
auto = Autodoc()
|
||||
|
||||
@user_routes.route('/api/users/getUserByToken/', defaults={'token': None})
|
||||
@user_routes.route('/api/users/getUserByToken/<string:token>', methods=["GET"])
|
||||
@auto.doc()
|
||||
def getUserByToken(token):
|
||||
"""
|
||||
<span class="card-title">>This Call will return a user by a given token</span>
|
||||
<br>
|
||||
<b>Route Parameters</b><br>
|
||||
- seToken: 'seToken'
|
||||
<br>
|
||||
<br>
|
||||
<b>Payload</b><br>
|
||||
- NONE
|
||||
<br>
|
||||
<br>
|
||||
<b>Response</b>
|
||||
<br>
|
||||
200 - JSON Example:<br>
|
||||
<code>
|
||||
{<br>
|
||||
'username': 'DarkLord',<br>
|
||||
'name': 'Darth Vader',<br>
|
||||
'email': 'darkLord@death.planet,<br>
|
||||
'isLecturer': 'True',<br>
|
||||
'seToken': 'xxxxxx-xxxxx-xxxxx-xxxxxx',<br>
|
||||
'avatar_url': 'http://location.git.com/somthing'<br>
|
||||
'isFirstLogin': False,<br>
|
||||
'campuses_id_list': ['JCA','JCB','JCC'],<br>
|
||||
'classes_id_list': ['a','b','c']<br>
|
||||
}
|
||||
</code>
|
||||
<br>
|
||||
403 - No User Found
|
||||
"""
|
||||
if token is None:
|
||||
return no_content("Token Is Empty, No User Found")
|
||||
|
||||
query = User.all()
|
||||
query.filter("seToken =", token)
|
||||
|
||||
for u in query.run(limit=5):
|
||||
return Response(response=u.to_JSON(),
|
||||
status=200,
|
||||
mimetype="application/json") # Real response!
|
||||
|
||||
return no_content("No User Found")
|
||||
|
||||
|
||||
@user_routes.route('/api/users/updateUser/<string:token>', methods=["POST"])
|
||||
|
||||
#----------------------------------------------------------
|
||||
# POST
|
||||
#----------------------------------------------------------
|
||||
|
||||
#----------------------------------------------------------
|
||||
# PUT
|
||||
#----------------------------------------------------------
|
||||
|
||||
@user_routes.route('/api/users/updateUser/<string:token>', methods=["PUT"])
|
||||
@auto.doc()
|
||||
def updateUser(token):
|
||||
"""
|
||||
|
@ -129,7 +93,318 @@ def updateUser(token):
|
|||
db.save
|
||||
return ok("User updated")
|
||||
|
||||
@user_routes.route('/api/users/addUserToCourse/<string:token>', methods=["PUT"])
|
||||
@auto.doc()
|
||||
def addUserToCourse(token):
|
||||
"""
|
||||
<span class="card-title">>This Call will add a course to user course list</span>
|
||||
<br>
|
||||
<b>Route Parameters</b><br>
|
||||
- seToken: 'seToken'
|
||||
<br>
|
||||
<br>
|
||||
<b>Payload</b><br>
|
||||
- JSON Object, Example: <br>
|
||||
{<br>
|
||||
'courseId': 1234567890<br>
|
||||
}<br>
|
||||
<br>
|
||||
<b>Response</b>
|
||||
<br>
|
||||
200 - User updated
|
||||
<br>
|
||||
400 - Bad Request
|
||||
"""
|
||||
|
||||
if not request.data:
|
||||
return bad_request()
|
||||
|
||||
try:
|
||||
payload = json.loads(request.data)
|
||||
except Exception as e:
|
||||
return bad_request()
|
||||
|
||||
#check user exists
|
||||
user = get_user_by_token(token)
|
||||
if user is None:
|
||||
return bad_request("Not a user!")
|
||||
|
||||
#check course Exists
|
||||
course = Course.get_by_id(payload['coursesId'])
|
||||
if course is None:
|
||||
return bad_request("No such Course!")
|
||||
|
||||
try:
|
||||
user.courses_id_list.append(payload['coursesId'])
|
||||
except Exception as e:
|
||||
print e
|
||||
return bad_request()
|
||||
|
||||
db.put(user)
|
||||
db.save
|
||||
return Response(response=user.to_JSON(),
|
||||
status=200,
|
||||
mimetype="application/json") # Real response!
|
||||
|
||||
|
||||
|
||||
|
||||
@user_routes.route('/api/users/addUserToCampus/<string:token>', methods=["PUT"])
|
||||
@auto.doc()
|
||||
def addUserToCampus(token):
|
||||
"""
|
||||
<span class="card-title">>This Call will add a Campus to user Campus list</span>
|
||||
<br>
|
||||
<b>Route Parameters</b><br>
|
||||
- seToken: 'seToken'
|
||||
<br>
|
||||
<br>
|
||||
<b>Payload</b><br>
|
||||
- JSON Object, Example: <br>
|
||||
{<br>
|
||||
'campusId': 1234567890<br>
|
||||
}<br>
|
||||
<br>
|
||||
<b>Response</b>
|
||||
<br>
|
||||
200 - User updated
|
||||
<br>
|
||||
400 - Bad Request
|
||||
"""
|
||||
|
||||
if not request.data:
|
||||
return bad_request()
|
||||
|
||||
try:
|
||||
payload = json.loads(request.data)
|
||||
except Exception as e:
|
||||
print e
|
||||
return bad_request()
|
||||
|
||||
if not is_lecturer(token): #todo: change to lecturer id
|
||||
return forbidden("Invalid token or not a lecturer!")
|
||||
|
||||
user = get_user_by_token(token)
|
||||
|
||||
#check Campus Exists
|
||||
campus = Campus.get_by_id(payload['campusId'])
|
||||
if campus is None:
|
||||
return bad_request("No such Campus!")
|
||||
|
||||
try:
|
||||
if str(payload['campusId']) in user.campuses_id_list:
|
||||
return accepted("Already a member of that campus")
|
||||
|
||||
user.campuses_id_list.append(str(payload['campusId']))
|
||||
except Exception as e:
|
||||
print e
|
||||
return bad_request()
|
||||
|
||||
|
||||
db.put(user)
|
||||
db.save
|
||||
return Response(response=user.to_JSON(),
|
||||
status=200,
|
||||
mimetype="application/json") # Real response!
|
||||
|
||||
|
||||
#----------------------------------------------------------
|
||||
# GET
|
||||
#----------------------------------------------------------
|
||||
|
||||
|
||||
@user_routes.route('/api/users/getUserByToken/', defaults={'token': None})
|
||||
@user_routes.route('/api/users/getUserByToken/<string:token>', methods=["GET"])
|
||||
@auto.doc()
|
||||
def getUserByToken(token):
|
||||
"""
|
||||
<span class="card-title">>This Call will return a user by a given token</span>
|
||||
<br>
|
||||
<b>Route Parameters</b><br>
|
||||
- seToken: 'seToken'
|
||||
<br>
|
||||
<br>
|
||||
<b>Payload</b><br>
|
||||
- NONE
|
||||
<br>
|
||||
<br>
|
||||
<b>Response</b>
|
||||
<br>
|
||||
200 - JSON Example:<br>
|
||||
<code>
|
||||
{<br>
|
||||
'username': 'DarkLord',<br>
|
||||
'name': 'Darth Vader',<br>
|
||||
'email': 'darkLord@death.planet,<br>
|
||||
'isLecturer': 'True',<br>
|
||||
'seToken': 'xxxxxx-xxxxx-xxxxx-xxxxxx',<br>
|
||||
'avatar_url': 'http://location.git.com/somthing'<br>
|
||||
'isFirstLogin': False,<br>
|
||||
'campuses_id_list': [{<br>
|
||||
'master_user_id': 111,<br>
|
||||
'id': 5629499534213120,<br>
|
||||
'email_ending': "@post.jce.ac.il",<br>
|
||||
'avatar_url': "https://yt3.ggpht.com/--ZkWxybWGOM/AAAAAAAAAAI/AAAAAAAAAAA/_nAICC_kzzI/s88-c-k-no/photo.jpg",<br>
|
||||
'title': "JCE"
|
||||
}],<br>
|
||||
'courses_id_list': ['a','b','c']<br>
|
||||
}
|
||||
</code>
|
||||
<br>
|
||||
403 - No User Found
|
||||
"""
|
||||
if token is None:
|
||||
return no_content("Token Is Empty, No User Found")
|
||||
|
||||
query = User.all()
|
||||
query.filter("seToken =", token)
|
||||
|
||||
for u in query.run(limit=5):
|
||||
for index, c in enumerate(u.campuses_id_list):
|
||||
c = json.loads(Campus.get_by_id(int(c)).to_JSON())
|
||||
u.campuses_id_list[index] = c
|
||||
|
||||
return Response(response=u.to_JSON(),
|
||||
status=200,
|
||||
mimetype="application/json") # Real response!
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
return no_content("No User Found")
|
||||
|
||||
|
||||
|
||||
#----------------------------------------------------------
|
||||
# DELETE
|
||||
#----------------------------------------------------------
|
||||
|
||||
@user_routes.route('/api/users/removeUserFromCampus/<string:token>/<string:campusId>', methods=["PUT"])
|
||||
@auto.doc()
|
||||
def removeUserFromCampus(token, campusId):
|
||||
"""
|
||||
<span class="card-title">>This Call will remove a Campus from a user Campus list</span>
|
||||
<br>
|
||||
<b>Route Parameters</b><br>
|
||||
- seToken: 'seToken'
|
||||
- 'campusId': 1234567890<br>
|
||||
<br>
|
||||
<br>
|
||||
<b>Payload</b><br>
|
||||
- NONE
|
||||
{<br>
|
||||
}<br>
|
||||
<br>
|
||||
<b>Response</b>
|
||||
<br>
|
||||
200 - User updated
|
||||
<br>
|
||||
400 - Bad Request
|
||||
"""
|
||||
|
||||
if not request.data:
|
||||
return bad_request()
|
||||
|
||||
try:
|
||||
payload = json.loads(request.data)
|
||||
except Exception as e:
|
||||
return bad_request()
|
||||
|
||||
if not is_lecturer(token): #todo: change to lecturer id
|
||||
return forbidden("Invalid token or not a lecturer!")
|
||||
|
||||
user = get_user_by_token(token)
|
||||
|
||||
#check Campus Exists
|
||||
campus = Campus.get_by_id(int(campusId))
|
||||
if campus is None:
|
||||
return bad_request("No such Campus!")
|
||||
|
||||
#check if user is owner of Campus
|
||||
if user.key().id() != campus.master_user_id:
|
||||
return forbidden("Lecturer is not owner of course")
|
||||
|
||||
try:
|
||||
user.campuses_id_list.remove(campusId)
|
||||
except Exception as e:
|
||||
print e
|
||||
return bad_request("user is not listed to this campus")
|
||||
|
||||
db.put(user)
|
||||
db.save
|
||||
return Response(response=user.to_JSON(),
|
||||
status=200,
|
||||
mimetype="application/json") # Real response!
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# @user_routes.route('/api/users/removeUserFromCourse/<string:token>/<string:courseId>', methods=["PUT"])
|
||||
# @auto.doc()
|
||||
# def removeUserFromCourse(token, courseId):
|
||||
# """
|
||||
# <span class="card-title">>This Call will remove a Course from a user Campus list</span>
|
||||
# <br>
|
||||
# <b>Route Parameters</b><br>
|
||||
# - seToken: 'seToken'
|
||||
# - 'courseId': 1234567890<br>
|
||||
# <br>
|
||||
# <br>
|
||||
# <b>Payload</b><br>
|
||||
# - NONE
|
||||
# {<br>
|
||||
# }<br>
|
||||
# <br>
|
||||
# <b>Response</b>
|
||||
# <br>
|
||||
# 200 - User updated
|
||||
# <br>
|
||||
# 400 - Bad Request
|
||||
# """
|
||||
#
|
||||
# if not request.data:
|
||||
# return bad_request()
|
||||
#
|
||||
# try:
|
||||
# payload = json.loads(request.data)
|
||||
# except Exception as e:
|
||||
# return bad_request()
|
||||
#
|
||||
# user = get_user_by_token(token)
|
||||
# if user is None:
|
||||
# return bad_request("No such user!")
|
||||
#
|
||||
#
|
||||
# #check Course Exists
|
||||
# course = Course.get_by_id(int(courseId))
|
||||
# if course is None:
|
||||
# return bad_request("No such Course!")
|
||||
#
|
||||
# #check if user is owner of Campus
|
||||
# if user.key().id() != course.master_id:
|
||||
# return forbidden("Lecturer is not owner of course")
|
||||
#
|
||||
# try:
|
||||
# user.campuses_id_list.remove(campusId)
|
||||
# except Exception as e:
|
||||
# print e
|
||||
# return bad_request("user is not listed to this campus")
|
||||
#
|
||||
# db.put(user)
|
||||
# db.save
|
||||
# return Response(response=user.to_JSON(),
|
||||
# status=200,
|
||||
# mimetype="application/json") # Real response!
|
||||
#
|
||||
#
|
||||
|
||||
|
||||
|
||||
#----------------------------------------------------------
|
||||
# DOCUMENTATION
|
||||
#----------------------------------------------------------
|
||||
|
||||
@user_routes.route('/api/users/help')
|
||||
def documentation():
|
||||
|
|
|
@ -14,18 +14,17 @@ class User(db.Model):
|
|||
isFirstLogin = db.BooleanProperty(default=True)
|
||||
campusName = db.StringProperty(required=True, default=" ")
|
||||
campuses_id_list = db.StringListProperty(default=[])
|
||||
classes_id_list = db.StringListProperty(default=[])
|
||||
courses_id_list = db.StringListProperty(default=[])
|
||||
|
||||
def to_JSON(self):
|
||||
data = {'username' : self.username,
|
||||
'name' : self.name,
|
||||
'email' : self.email,
|
||||
'isLecturer' : self.isLecturer,
|
||||
'seToken' : self.seToken,
|
||||
'avatar_url' : self.avatar_url,
|
||||
'isFirstLogin' : self.isFirstLogin,
|
||||
'campusName': self.campusName,
|
||||
'campuses_id_list': self.campuses_id_list,
|
||||
'classes_id_list': self.classes_id_list
|
||||
'courses_id_list': self.courses_id_list
|
||||
}
|
||||
return json.dumps(data)
|
||||
|
|
|
@ -30,12 +30,14 @@ angular.module('SeHub')
|
|||
$scope.createCourseClicked = function()
|
||||
{
|
||||
$scope.isNewCourse = true;
|
||||
$scope.showMyClass = false;
|
||||
console.log("create course Clicked!!");
|
||||
}
|
||||
|
||||
$scope.showMyCourses = function()
|
||||
{
|
||||
$scope.showMyClass = true;
|
||||
$scope.isNewCourse = false;
|
||||
}
|
||||
|
||||
$scope.submitNewClassClicked = function()
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
angular.module('SeHub')
|
||||
.controller('projectsContoller', ['$scope', '$cookies', '$cookieStore', '$window', '$location', '$mdToast', '$mdDialog', 'apiService', '$rootScope', function ($scope, $cookies, $cookieStore, $window, $location, $mdToast, $mdDialog, apiService ,$rootScope)
|
||||
.controller('projectsController', ['$scope', '$cookies', '$cookieStore', '$window', '$location', '$mdToast', '$mdDialog', 'apiService', '$rootScope', function ($scope, $cookies, $cookieStore, $window, $location, $mdToast, $mdDialog, apiService ,$rootScope)
|
||||
{
|
||||
console.log("in projects controller");
|
||||
|
||||
|
|
|
@ -27,6 +27,12 @@
|
|||
<input ng-model="task.date" type="text"/>
|
||||
</datepicker>
|
||||
</div>
|
||||
<div layout="row">
|
||||
<div flex="60"></div>
|
||||
<div>
|
||||
<md-button class="md-raised md-primary"><i class="fa fa-paper-plane"></i> Submit</md-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -55,8 +61,8 @@
|
|||
</md-switch>
|
||||
</div>
|
||||
<div layout="row">
|
||||
<div flex="70"></div>
|
||||
<md-button class="md-primary" ng-click="addComponent()">Add Component</md-button>
|
||||
<div flex="65"></div>
|
||||
<md-button class="md-raised" ng-click="addComponent()"><i class="fa fa-plus"></i> Add To Task</md-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
Loading…
Reference in a new issue