Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Fullname and Nickname Feature to User Profiles #2575

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions mslib/index.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@ def create_app(name="", imprint=None, gdpr=None):

@APP.route('/xstatic/<name>/<path:filename>')
def files(name, filename):

base_path = _xstatic(name)
if base_path is None:
abort(404)
Expand All @@ -96,10 +95,6 @@ def mss_theme(filename):

APP.jinja_env.globals.update(get_topmenu=get_topmenu)

@APP.route("/index")
def index():
return render_template("/index.html")

@APP.route("/mss/about")
@APP.route("/mss")
def about():
Expand Down
5 changes: 5 additions & 0 deletions mslib/mscolab/file_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,8 @@ def modify_user(self, user, attribute=None, value=None, action=None):
if action == "create":
user_query = User.query.filter_by(emailid=str(user.emailid)).first()
if user_query is None:
if not user.full_name or not user.nickname:
raise ValueError("Full name and nickname must be provided.")
db.session.add(user)
db.session.commit()
else:
Expand Down Expand Up @@ -254,6 +256,9 @@ def modify_user(self, user, attribute=None, value=None, action=None):
user_query = User.query.filter_by(emailid=str(value)).first()
if user_query is not None:
return False
if attribute in ["full_name", "nickname"]:
if not value.strip():
raise ValueError(f"{attribute} cannot be empty.")
setattr(user, attribute, value)
db.session.commit()
return True
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"""To version <next-major-version>

Revision ID: a63201ddec7a
Revises: 922e4d9c94e2
Create Date: 2024-11-28 14:11:53.302308

"""
from alembic import op
import sqlalchemy as sa
import mslib.mscolab.custom_migration_types as cu


# revision identifiers, used by Alembic.
revision = 'a63201ddec7a'
down_revision = '922e4d9c94e2'
branch_labels = None
depends_on = None


def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('users', schema=None) as batch_op:
batch_op.add_column(sa.Column('fullname', sa.String(length=255), nullable=True))
batch_op.add_column(sa.Column('nickname', sa.String(length=255), nullable=True))

# ### end Alembic commands ###


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('users', schema=None) as batch_op:
batch_op.drop_column('nickname')
batch_op.drop_column('fullname')

# ### end Alembic commands ###
8 changes: 7 additions & 1 deletion mslib/mscolab/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,11 @@ class User(db.Model):
permissions = db.relationship('Permission', cascade='all,delete,delete-orphan', backref='user')
authentication_backend = db.Column(db.String(255), nullable=False, default='local')

fullname = db.Column(db.String(255), nullable=True)
Copy link
Member

@ReimarBauer ReimarBauer Nov 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because you change the dbase model, you need to do additional steps, see
https://github.com/Open-MSS/MSS/blob/develop/docs/development.rst#changing-the-database-model

when that is not done, a annapurna-gupta/MSS$ python mslib/mscolab/mscolab.py db --seed crashes with
sqlite3.OperationalError: table users has no column named fullname

also it may be useful to change the seed.py to add some of this data.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i have generated the migration script but while applying it to database i am facing import error.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please show the failure

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Screenshot 2024-11-30 230514

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please show your PYTHONPATH

Copy link
Member

@ReimarBauer ReimarBauer Dec 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

likly PYTHONPATH should show D:\LOCAL_CODE COPY\MSS

I think on windows you can use setx PYTHONPATH D:\LOCAL_CODE COPY\MSS

currently python can't find mslib. ...

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Screenshot 2024-12-02 172456

nickname = db.Column(db.String(255), nullable=True)

def __init__(self, emailid, username, password, profile_image_path=None, confirmed=False,
confirmed_on=None, authentication_backend='local'):
confirmed_on=None, authentication_backend='local', fullname="", nickname=""):
self.username = str(username)
self.emailid = str(emailid)
self.hash_password(password)
Expand All @@ -76,6 +79,9 @@ def __init__(self, emailid, username, password, profile_image_path=None, confirm
self.confirmed_on = confirmed_on
self.authentication_backend = str(authentication_backend)

self.fullname = str(fullname) if fullname else None
self.nickname = str(nickname) if nickname else None

def __repr__(self):
return f'<User {self.username}>'

Expand Down
44 changes: 39 additions & 5 deletions mslib/mscolab/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,9 +248,9 @@ def check_login(emailid, password):
return False


def register_user(email, password, username):
if len(str(email.strip())) == 0 or len(str(username.strip())) == 0:
return {"success": False, "message": "Your username or email cannot be empty"}
def register_user(email, password, username, fullname, nickname):
if len(str(email.strip())) == 0 or len(str(username.strip())) == 0 or len(str(fullname.strip())) == 0 or len(str(username.strip())) == 0:
return {"success": False, "message": "Your username, fullname, nickname or email cannot be empty"}
is_valid_username = True if username.find("@") == -1 else False
is_valid_email = validate_email(email)
if not is_valid_email:
Expand All @@ -263,11 +263,10 @@ def register_user(email, password, username):
user_exists = User.query.filter_by(username=str(username)).first()
if user_exists:
return {"success": False, "message": "This username is already registered"}
user = User(email, username, password)
user = User(email, username, password, fullname=fullname, nickname=nickname)
result = fm.modify_user(user, action="create")
return {"success": result}


def verify_user(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
Expand Down Expand Up @@ -616,6 +615,40 @@ def set_version_name():
return jsonify({"success": True, "message": "Successfully set version name"})


@APP.route("/edit_user_info", methods=["POST"])
@verify_user
def edit_user_info():
user = g.user
fullname = request.form.get("fullname")
nickname = request.form.get("nickname")

try:
# Update the user's full name and nickname in the database
user_record = User.query.filter_by(id=int(user.id)).first()
if user_record is None:
return jsonify({"success": False, "error": "User not found."}), 404

# Update fields
user_record.fullname = fullname # Update full name
user_record.nickname = nickname # Update nickname

# Commit changes to the database
db.session.commit()
return jsonify({
"success": True,
"fullname": user_record.fullname,
"nickname": user_record.nickname
}), 200

except Exception as e:
logging.debug(f"Error updating user info: {str(e)}")
return jsonify({
"success": False,
"error": "Failed to update user info"
}), 500



@APP.route('/authorized_users', methods=['GET'])
@verify_user
def authorized_users():
Expand Down Expand Up @@ -1027,3 +1060,4 @@ def main():

if __name__ == '__main__':
main()

26 changes: 26 additions & 0 deletions mslib/msui/mscolab.py
Original file line number Diff line number Diff line change
Expand Up @@ -966,6 +966,32 @@ def delete_account(self, _=None):
if r.status_code == 200 and json.loads(r.text)["success"] is True:
self.logout()

@verify_token_required
def editfull_name(self):
fullname, ok = QtWidgets.QInputDialog.getText(
self.ui,
self.ui.tr("Edit Full Name"),
self.ui.tr(
f"You're about to change the full name - '{self.active_operation_name}' "
f"Enter new full name: "
),
)
if ok:
data = {
"token": self.token,
"fullname": str(fullname)
}
url = url_join(self.mscolab_server_url, 'edit_full_name')
r = requests.post(url, data=data)
if r.text == "true":
self.error_dialog = QtWidgets.QErrorMessage()
self.error_dialog.showMessage("Fullname is updated successfully.")
self.profile_dialog.fullname_label2.setText(self.user["fullname"])


def editnick_name(self):
pass

@verify_user_token
def add_operation_handler(self, _=None):
def check_and_enable_operation_accept():
Expand Down
54 changes: 35 additions & 19 deletions mslib/msui/qt5/ui_add_user_dialog.py
Original file line number Diff line number Diff line change
@@ -1,52 +1,66 @@
# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'mslib/msui/ui/ui_add_user.ui'
# Form implementation generated from reading ui file 'ui_add_user_dialog.ui'
#
# Created by: PyQt5 UI code generator 5.9.2
# Created by: PyQt5 UI code generator 5.15.9
#
# WARNING! All changes made in this file will be lost!
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.


from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_addUserDialog(object):
def setupUi(self, addUserDialog):
addUserDialog.setObjectName("addUserDialog")
addUserDialog.resize(375, 232)
addUserDialog.resize(375, 293)
self.gridLayout = QtWidgets.QGridLayout(addUserDialog)
self.gridLayout.setObjectName("gridLayout")
self.verticalLayout = QtWidgets.QVBoxLayout()
self.verticalLayout.setObjectName("verticalLayout")
self.formLayout = QtWidgets.QFormLayout()
self.formLayout.setFieldGrowthPolicy(QtWidgets.QFormLayout.ExpandingFieldsGrow)
self.formLayout.setLabelAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.formLayout.setHorizontalSpacing(7)
self.formLayout.setVerticalSpacing(14)
self.formLayout.setObjectName("formLayout")
self.usernameLabel = QtWidgets.QLabel(addUserDialog)
self.usernameLabel.setObjectName("usernameLabel")
self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.usernameLabel)
self.username = QtWidgets.QLineEdit(addUserDialog)
self.username.setObjectName("username")
self.formLayout.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.username)
self.fullnamelabel = QtWidgets.QLabel(addUserDialog)
self.fullnamelabel.setObjectName("fullnamelabel")
self.formLayout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.fullnamelabel)
self.fullname = QtWidgets.QLineEdit(addUserDialog)
self.fullname.setText("")
self.fullname.setObjectName("fullname")
self.formLayout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.fullname)
self.emailIDLabel = QtWidgets.QLabel(addUserDialog)
self.emailIDLabel.setObjectName("emailIDLabel")
self.formLayout.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.emailIDLabel)
self.emailid = QtWidgets.QLineEdit(addUserDialog)
self.emailid.setObjectName("emailid")
self.formLayout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.emailid)
self.formLayout.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.emailid)
self.passwordLabel = QtWidgets.QLabel(addUserDialog)
self.passwordLabel.setObjectName("passwordLabel")
self.formLayout.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.passwordLabel)
self.formLayout.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.passwordLabel)
self.password = QtWidgets.QLineEdit(addUserDialog)
self.password.setEchoMode(QtWidgets.QLineEdit.Password)
self.password.setObjectName("password")
self.formLayout.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.password)
self.formLayout.setWidget(4, QtWidgets.QFormLayout.FieldRole, self.password)
self.confirmPasswordLabel = QtWidgets.QLabel(addUserDialog)
self.confirmPasswordLabel.setObjectName("confirmPasswordLabel")
self.formLayout.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.confirmPasswordLabel)
self.formLayout.setWidget(5, QtWidgets.QFormLayout.LabelRole, self.confirmPasswordLabel)
self.rePassword = QtWidgets.QLineEdit(addUserDialog)
self.rePassword.setEchoMode(QtWidgets.QLineEdit.Password)
self.rePassword.setObjectName("rePassword")
self.formLayout.setWidget(4, QtWidgets.QFormLayout.FieldRole, self.rePassword)
self.emailIDLabel = QtWidgets.QLabel(addUserDialog)
self.emailIDLabel.setObjectName("emailIDLabel")
self.formLayout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.emailIDLabel)
self.usernameLabel = QtWidgets.QLabel(addUserDialog)
self.usernameLabel.setObjectName("usernameLabel")
self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.usernameLabel)
self.formLayout.setWidget(5, QtWidgets.QFormLayout.FieldRole, self.rePassword)
self.verticalLayout_2 = QtWidgets.QVBoxLayout()
self.verticalLayout_2.setObjectName("verticalLayout_2")
self.formLayout.setLayout(6, QtWidgets.QFormLayout.FieldRole, self.verticalLayout_2)
self.verticalLayout.addLayout(self.formLayout)
self.buttonBox = QtWidgets.QDialogButtonBox(addUserDialog)
self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
Expand All @@ -56,18 +70,20 @@ def setupUi(self, addUserDialog):
self.gridLayout.addLayout(self.verticalLayout, 0, 0, 1, 1)

self.retranslateUi(addUserDialog)
self.buttonBox.accepted.connect(addUserDialog.accept)
self.buttonBox.rejected.connect(addUserDialog.reject)
self.buttonBox.accepted.connect(addUserDialog.accept) # type: ignore
self.buttonBox.rejected.connect(addUserDialog.reject) # type: ignore
QtCore.QMetaObject.connectSlotsByName(addUserDialog)

def retranslateUi(self, addUserDialog):
_translate = QtCore.QCoreApplication.translate
addUserDialog.setWindowTitle(_translate("addUserDialog", "Add user"))
self.usernameLabel.setText(_translate("addUserDialog", "Username:"))
self.username.setPlaceholderText(_translate("addUserDialog", "John Doe"))
self.fullnamelabel.setText(_translate("addUserDialog", "Fullname:"))
self.fullname.setPlaceholderText(_translate("addUserDialog", "John Michael Doe"))
self.emailIDLabel.setText(_translate("addUserDialog", "Email:"))
self.emailid.setPlaceholderText(_translate("addUserDialog", "[email protected]"))
self.passwordLabel.setText(_translate("addUserDialog", "Password:"))
self.password.setPlaceholderText(_translate("addUserDialog", "Your password"))
self.confirmPasswordLabel.setText(_translate("addUserDialog", "Confirm Password:"))
self.rePassword.setPlaceholderText(_translate("addUserDialog", "Confirm your password"))
self.emailIDLabel.setText(_translate("addUserDialog", "Email:"))
self.usernameLabel.setText(_translate("addUserDialog", "Username:"))
Loading