mirror of
https://github.com/DragonMinded/bemaniutils.git
synced 2026-08-05 01:18:42 -05:00
Implement admin tool to list profiles on admin user page, including ability to delete a profile.
This commit is contained in:
parent
552f64af0a
commit
35b939bc64
|
|
@ -21,6 +21,21 @@ class GameConstants(Enum):
|
|||
REFLEC_BEAT = "reflec"
|
||||
SDVX = "sdvx"
|
||||
|
||||
@classmethod
|
||||
def LUT(cls) -> Dict[str, str]:
|
||||
return {
|
||||
cls.BISHI_BASHI.value: "BishiBashi",
|
||||
cls.DANCE_EVOLUTION.value: "Dance Evolution",
|
||||
cls.DDR.value: "DDR",
|
||||
cls.IIDX.value: "IIDX",
|
||||
cls.JUBEAT.value: "Jubeat",
|
||||
cls.MGA.value: "Metal Gear Arcade",
|
||||
cls.MUSECA.value: "MÚSECA",
|
||||
cls.POPN_MUSIC.value: "Pop'n Music",
|
||||
cls.REFLEC_BEAT.value: "Reflec Beat",
|
||||
cls.SDVX.value: "SDVX",
|
||||
}
|
||||
|
||||
|
||||
class VersionConstants:
|
||||
"""
|
||||
|
|
@ -476,7 +491,7 @@ class _RegionConstants:
|
|||
|
||||
raise Exception(f"Unexpected value {region} for game region!")
|
||||
|
||||
@property
|
||||
@classmethod
|
||||
def LUT(cls) -> Dict[int, str]:
|
||||
return {
|
||||
cls.HOKKAIDO: "北海道 (Hokkaido)",
|
||||
|
|
|
|||
|
|
@ -529,6 +529,38 @@ class UserData(BaseData):
|
|||
self.deserialize(result["data"]),
|
||||
)
|
||||
|
||||
def get_profiles(self, userid: UserID) -> List[Profile]:
|
||||
"""
|
||||
Given a user ID, look up all associated profiles across all games and versions. If the user
|
||||
does not have any profiles then returns an empty list.
|
||||
|
||||
Parameters:
|
||||
userid - Integer user ID, as looked up by one of the above functions.
|
||||
|
||||
Returns:
|
||||
A list of valid profiles that were pulled from the DB.
|
||||
"""
|
||||
sql = """
|
||||
SELECT refid.refid AS refid, extid.extid AS extid, refid.game AS game, refid.version AS version, profile.data AS data
|
||||
FROM refid, extid, profile
|
||||
WHERE
|
||||
refid.userid = :userid AND
|
||||
extid.userid = refid.userid AND
|
||||
extid.game = refid.game AND
|
||||
profile.refid = refid.refid
|
||||
"""
|
||||
cursor = self.execute(sql, {"userid": userid})
|
||||
return [
|
||||
Profile(
|
||||
GameConstants(result["game"]),
|
||||
result["version"],
|
||||
result["refid"],
|
||||
result["extid"],
|
||||
self.deserialize(result["data"]),
|
||||
)
|
||||
for result in cursor.mappings()
|
||||
]
|
||||
|
||||
def get_any_profile(self, game: GameConstants, version: int, userid: UserID) -> Optional[Profile]:
|
||||
"""
|
||||
Given a game/version/userid, look up the associated profile. If the profile for that version
|
||||
|
|
@ -780,6 +812,12 @@ class UserData(BaseData):
|
|||
sql = "DELETE FROM profile WHERE refid = :refid LIMIT 1"
|
||||
self.execute(sql, {"refid": refid})
|
||||
|
||||
# Now delete any associated data items that went with that profile.
|
||||
sql = "DELETE FROM achievement WHERE refid = :refid"
|
||||
self.execute(sql, {"refid": refid})
|
||||
sql = "DELETE FROM time_based_achievement WHERE refid = :refid"
|
||||
self.execute(sql, {"refid": refid})
|
||||
|
||||
def get_achievement(
|
||||
self,
|
||||
game: GameConstants,
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ from bemani.common import (
|
|||
GameConstants,
|
||||
RegionConstants,
|
||||
ValidatedDict,
|
||||
Profile,
|
||||
ID,
|
||||
)
|
||||
from bemani.data import Arcade, Machine, User, UserID, News, Event, Server, Client
|
||||
from bemani.data.api.client import APIClient, NotAuthorizedAPIException, APIException
|
||||
|
|
@ -98,6 +100,16 @@ def format_user(user: User) -> Dict[str, Any]:
|
|||
}
|
||||
|
||||
|
||||
def format_profile(profile: Profile) -> Dict[str, object]:
|
||||
return {
|
||||
"game": profile.game.value,
|
||||
"version": profile.version,
|
||||
"extid": ID.format_extid(profile.extid),
|
||||
"refid": profile.refid,
|
||||
"name": profile.get_str("name", ""),
|
||||
}
|
||||
|
||||
|
||||
def format_news(news: News) -> Dict[str, Any]:
|
||||
return {
|
||||
"id": news.id,
|
||||
|
|
@ -145,7 +157,7 @@ def viewsettings() -> Response:
|
|||
**{
|
||||
"title": "Network Settings",
|
||||
"config": g.config,
|
||||
"region": RegionConstants.LUT,
|
||||
"region": RegionConstants.LUT(),
|
||||
},
|
||||
)
|
||||
)
|
||||
|
|
@ -240,7 +252,7 @@ def viewarcades() -> Response:
|
|||
"admin/arcades.react.js",
|
||||
{
|
||||
"arcades": [format_arcade(arcade) for arcade in g.data.local.machine.get_all_arcades()],
|
||||
"regions": RegionConstants.LUT,
|
||||
"regions": RegionConstants.LUT(),
|
||||
"usernames": g.data.local.user.get_all_usernames(),
|
||||
"paseli_enabled": g.config.paseli.enabled,
|
||||
"paseli_infinite": g.config.paseli.infinite,
|
||||
|
|
@ -271,17 +283,7 @@ def viewmachines() -> Response:
|
|||
{
|
||||
"machines": [format_machine(machine) for machine in g.data.local.machine.get_all_machines()],
|
||||
"arcades": {arcade.id: arcade.name for arcade in g.data.local.machine.get_all_arcades()},
|
||||
"series": {
|
||||
GameConstants.BISHI_BASHI.value: "BishiBashi",
|
||||
GameConstants.DDR.value: "DDR",
|
||||
GameConstants.IIDX.value: "IIDX",
|
||||
GameConstants.JUBEAT.value: "Jubeat",
|
||||
GameConstants.MGA.value: "Metal Gear Arcade",
|
||||
GameConstants.MUSECA.value: "MÚSECA",
|
||||
GameConstants.POPN_MUSIC.value: "Pop'n Music",
|
||||
GameConstants.REFLEC_BEAT.value: "Reflec Beat",
|
||||
GameConstants.SDVX.value: "SDVX",
|
||||
},
|
||||
"series": GameConstants.LUT(),
|
||||
"games": games,
|
||||
"enforcing": g.config.server.enforce_pcbid,
|
||||
},
|
||||
|
|
@ -374,7 +376,15 @@ def viewuser(userid: int) -> Response:
|
|||
except CardCipherException:
|
||||
return "????????????????"
|
||||
|
||||
# Get list of all game names and versions.
|
||||
games: Dict[str, Dict[int, str]] = {}
|
||||
for game, version, name in Base.all_games():
|
||||
if game.value not in games:
|
||||
games[game.value] = {}
|
||||
games[game.value][version] = name
|
||||
|
||||
cards = [__format_card(card) for card in g.data.local.user.get_cards(userid)]
|
||||
profiles = [format_profile(profile) for profile in g.data.local.user.get_profiles(userid)]
|
||||
arcades = g.data.local.machine.get_all_arcades()
|
||||
return render_react(
|
||||
"User",
|
||||
|
|
@ -384,7 +394,9 @@ def viewuser(userid: int) -> Response:
|
|||
"email": user.email,
|
||||
"username": user.username,
|
||||
},
|
||||
"games": games,
|
||||
"cards": cards,
|
||||
"profiles": profiles,
|
||||
"arcades": {arcade.id: arcade.name for arcade in arcades},
|
||||
"balances": {arcade.id: g.data.local.user.get_balance(userid, arcade.id) for arcade in arcades},
|
||||
"events": [
|
||||
|
|
@ -395,6 +407,7 @@ def viewuser(userid: int) -> Response:
|
|||
{
|
||||
"refresh": url_for("admin_pages.listuser", userid=userid),
|
||||
"removeusercard": url_for("admin_pages.removeusercard", userid=userid),
|
||||
"removeuserprofile": url_for("admin_pages.removeuserprofile", userid=userid),
|
||||
"addusercard": url_for("admin_pages.addusercard", userid=userid),
|
||||
"updatebalance": url_for("admin_pages.updatebalance", userid=userid),
|
||||
"updateusername": url_for("admin_pages.updateusername", userid=userid),
|
||||
|
|
@ -1078,6 +1091,34 @@ def removeusercard(userid: int) -> Dict[str, Any]:
|
|||
}
|
||||
|
||||
|
||||
@admin_pages.route("/users/<int:userid>/profiles/remove", methods=["POST"])
|
||||
@jsonify
|
||||
@adminrequired
|
||||
def removeuserprofile(userid: int) -> Dict[str, Any]:
|
||||
# Cast the userID.
|
||||
userid = UserID(userid)
|
||||
|
||||
# Grab refid, see if it exists.
|
||||
refid = request.get_json()["refid"]
|
||||
user = g.data.local.user.get_user(userid)
|
||||
|
||||
# Make sure the user ID is valid
|
||||
if user is None:
|
||||
raise Exception("Cannot find user to update!")
|
||||
|
||||
# Make sure the profile is valid.
|
||||
profiles = [p for p in g.data.local.user.get_profiles(userid) if p.refid == refid]
|
||||
if len(profiles) != 1:
|
||||
raise Exception("Cannot find profile to delete!")
|
||||
profile = profiles[0]
|
||||
|
||||
# Remove it from the user's account
|
||||
g.data.local.user.delete_profile(profile.game, profile.version, userid)
|
||||
|
||||
# Return new profile list
|
||||
return {"profiles": [format_profile(profile) for profile in g.data.local.user.get_profiles(userid)]}
|
||||
|
||||
|
||||
@admin_pages.route("/users/<int:userid>/cards/add", methods=["POST"])
|
||||
@jsonify
|
||||
@adminrequired
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ def viewarcade(arcadeid: int) -> Response:
|
|||
"arcade/arcade.react.js",
|
||||
{
|
||||
"arcade": format_arcade(arcade),
|
||||
"regions": RegionConstants.LUT,
|
||||
"regions": RegionConstants.LUT(),
|
||||
"machines": machines,
|
||||
"game_settings": get_game_settings(g.data, arcadeid),
|
||||
"balances": {balance[0]: balance[1] for balance in g.data.local.machine.get_balances(arcadeid)},
|
||||
|
|
|
|||
|
|
@ -324,7 +324,7 @@ def viewsettings() -> Response:
|
|||
"iidx/settings.react.js",
|
||||
{
|
||||
"player": djinfo,
|
||||
"regions": RegionConstants.LUT,
|
||||
"regions": RegionConstants.LUT(),
|
||||
"versions": {version: name for (game, version, name) in frontend.all_games()},
|
||||
"qpros": frontend.get_all_items(versions),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ var user_management = createReactClass({
|
|||
new_password1: '',
|
||||
new_password2: '',
|
||||
cards: window.cards,
|
||||
profiles: window.profiles,
|
||||
new_card: '',
|
||||
balances: window.balances,
|
||||
credits: credits,
|
||||
|
|
@ -392,6 +393,49 @@ var user_management = createReactClass({
|
|||
);
|
||||
},
|
||||
|
||||
deleteExistingProfile: function(event, refid) {
|
||||
$.confirm({
|
||||
escapeKey: 'Cancel',
|
||||
animation: 'none',
|
||||
closeAnimation: 'none',
|
||||
title: 'Delete Profile',
|
||||
content: 'Are you sure you want to delete this profile?',
|
||||
buttons: {
|
||||
Delete: {
|
||||
btnClass: 'delete',
|
||||
action: function() {
|
||||
AJAX.post(
|
||||
Link.get('removeuserprofile'),
|
||||
{refid: refid},
|
||||
function(response) {
|
||||
// Kill the entry we no longer need
|
||||
var profiles = this.state.profiles;
|
||||
this.setState({
|
||||
profiles: profiles.filter((prof) => prof.refid != refid),
|
||||
});
|
||||
}.bind(this)
|
||||
);
|
||||
}.bind(this),
|
||||
},
|
||||
Cancel: function() {
|
||||
},
|
||||
}
|
||||
});
|
||||
event.preventDefault();
|
||||
},
|
||||
|
||||
renderDeleteProfileButton: function(profile) {
|
||||
return (
|
||||
<>
|
||||
<Delete
|
||||
onClick={function(event) {
|
||||
this.deleteExistingProfile(event, profile.refid);
|
||||
}.bind(this)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<div>
|
||||
|
|
@ -430,6 +474,47 @@ var user_management = createReactClass({
|
|||
<input type="submit" value="add card" />
|
||||
</form>
|
||||
</div>
|
||||
<div className="section">
|
||||
<h3>Profiles</h3>
|
||||
{ this.state.profiles.length == 0 ?
|
||||
<div>
|
||||
<span className="placeholder">No profiles present!</span>
|
||||
</div> :
|
||||
<Table
|
||||
className="list profile"
|
||||
columns={[
|
||||
{
|
||||
name: 'Game',
|
||||
render: function(profile) {
|
||||
return window.games[profile.game][profile.version]
|
||||
}.bind(this),
|
||||
sort: function(a, b) {
|
||||
return a.game.localeCompare(b.game);
|
||||
}.bind(this),
|
||||
},
|
||||
{
|
||||
name: 'Game ID',
|
||||
render: function(profile) {
|
||||
return profile.extid;
|
||||
}.bind(this),
|
||||
},
|
||||
{
|
||||
name: 'Name',
|
||||
render: function(profile) {
|
||||
return profile.name;
|
||||
}.bind(this),
|
||||
},
|
||||
{
|
||||
name: '',
|
||||
render: this.renderDeleteProfileButton,
|
||||
action: true,
|
||||
},
|
||||
]}
|
||||
rows={this.state.profiles}
|
||||
emptymessage="There are no profiles associated with this user."
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
<div className="section">
|
||||
<h3>PASELI Balance</h3>
|
||||
{ Object.keys(this.state.arcades).length == 0 ?
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user