from flask import render_template, Blueprint, redirect, url_for, request, abort, flash, make_response, current_app from flask_user import login_required, current_user from datatables import ColumnDT, DataTables import time from app.models import CharacterInfo, CharacterXML, Account, db from app.forms import RescueForm, CharXMLUploadForm from app import gm_level, log_audit from app.luclient import translate_from_locale import xmltodict import xml.etree.ElementTree as ET import json from xml.dom import minidom character_blueprint = Blueprint('characters', __name__) @character_blueprint.route('/', methods=['GET']) @login_required @gm_level(3) def index(): return render_template('character/index.html.j2') @character_blueprint.route('/approve_name//', methods=['GET']) @login_required @gm_level(3) def approve_name(id, action): character = CharacterInfo.query.filter(CharacterInfo.id == id).first() if action == "approve": if character.pending_name: character.name = character.pending_name character.pending_name = "" log_audit(f"Approved ({character.id}){character.pending_name} from {character.name}") flash( f"Approved ({character.id}){character.pending_name} from {character.name}", "success" ) else: log_audit("Cannot make character name empty") flash( "Cannot make character name empty", "danger" ) character.needs_rename = False elif action == "rename": character.needs_rename = True log_audit( f"Marked character ({character.id}){character.name} \ (Pending Name: {character.pending_name if character.pending_name else 'None'}) as needing Rename") flash( f"Marked character {character.name} \ (Pending Name: {character.pending_name if character.pending_name else 'None'}) as needing Rename", "danger" ) character.save() return redirect(request.referrer if request.referrer else url_for("main.index")) @character_blueprint.route('/view/', methods=['GET']) @login_required def view(id): character_data = CharacterInfo.query.filter(CharacterInfo.id == id).first() if character_data == {}: abort(404) return if current_user.gm_level < 3: if character_data.account_id and character_data.account_id != current_user.id: abort(403) return character_json = xmltodict.parse( CharacterXML.query.filter( CharacterXML.id == id ).first().xml_data.replace("\"stt=", "\" stt="), attr_prefix="attr_" ) # print json for reference # with open("errorchar.json", "a") as file: # file.write( # json.dumps(character_json, indent=4) # ) # stupid fix for jinja parsing character_json["obj"]["inv"]["holdings"] = character_json["obj"]["inv"].pop("items") # sort by items slot index if type(character_json["obj"]["inv"]["holdings"]["in"]) == list: for inv in character_json["obj"]["inv"]["holdings"]["in"]: if "i" in inv.keys() and type(inv["i"]) == list: inv["i"] = sorted(inv["i"], key=lambda i: int(i['attr_s'])) return render_template( 'character/view.html.j2', character_data=character_data, character_json=character_json ) @character_blueprint.route('/chardata/', methods=['GET']) @login_required def chardata(id): character_data = CharacterInfo.query.filter(CharacterInfo.id == id).first() if character_data == {}: abort(404) return if current_user.gm_level < 3: if character_data.account_id and character_data.account_id != current_user.id: abort(403) return character_json = xmltodict.parse( CharacterXML.query.filter( CharacterXML.id == id ).first().xml_data.replace("\"stt=", "\" stt="), attr_prefix="attr_" ) # print json for reference # with open("errorchar.json", "a") as file: # file.write( # json.dumps(character_json, indent=4) # ) # stupid fix for jinja parsing character_json["obj"]["inv"]["holdings"] = character_json["obj"]["inv"].pop("items") # sort by items slot index if type(character_json["obj"]["inv"]["holdings"]["in"]) == list: for inv in character_json["obj"]["inv"]["holdings"]["in"]: if "i" in inv.keys() and type(inv["i"]) == list: inv["i"] = sorted(inv["i"], key=lambda i: int(i['attr_s'])) return render_template( 'partials/_charxml.html.j2', character_data=character_data, character_json=character_json ) @character_blueprint.route('/inventory//', methods=['GET']) @login_required def inventory(id, inventory_id): character_data = CharacterInfo.query.filter(CharacterInfo.id == id).first() if character_data == {}: abort(404) return if current_user.gm_level < 3: if character_data.account_id and character_data.account_id != current_user.id: abort(403) return character_json = xmltodict.parse( CharacterXML.query.filter( CharacterXML.id == id ).first().xml_data.replace("\"stt=", "\" stt="), attr_prefix="attr_" ) # print json for reference # with open("errorchar.json", "a") as file: # file.write( # json.dumps(character_json, indent=4) # ) # stupid fix for jinja parsing character_json["obj"]["inv"]["holdings"] = character_json["obj"]["inv"].pop("items") # sort by items slot index if type(character_json["obj"]["inv"]["holdings"]["in"]) == list: for inv in character_json["obj"]["inv"]["holdings"]["in"]: if "i" in inv.keys() and type(inv["i"]) == list: inv["i"] = sorted(inv["i"], key=lambda i: int(i['attr_s'])) for inventory in character_json["obj"]["inv"]["holdings"]["in"]: if inventory["attr_t"] == inventory_id: return render_template( 'partials/charxml/_inventory.html.j2', inventory=inventory ) return "No Items in Inventory", 404 @character_blueprint.route('/view_xml/', methods=['GET']) @login_required def view_xml(id): character_data = CharacterInfo.query.filter(CharacterInfo.id == id).first() if character_data == {}: abort(404) return if current_user.gm_level < 3: if character_data.account_id and character_data.account_id != current_user.id: abort(403) return character_xml = CharacterXML.query.filter( CharacterXML.id == id ).first().xml_data.replace("\"stt=", "\" stt=") response = make_response(character_xml) response.headers.set('Content-Type', 'text/xml') return response @character_blueprint.route('/get_xml/', methods=['GET']) @login_required def get_xml(id): character_data = CharacterInfo.query.filter(CharacterInfo.id == id).first() if character_data == {}: abort(404) return if current_user.gm_level < 3: if character_data.account_id and character_data.account_id != current_user.id: abort(403) return character_xml = CharacterXML.query.filter( CharacterXML.id == id ).first().xml_data response = make_response(character_xml) response.headers.set('Content-Type', 'attachment/xml') response.headers.set( 'Content-Disposition', 'attachment', filename=f"{character_data.name}.xml" ) return response @character_blueprint.route('/restrict//', methods=['GET']) @login_required @gm_level(3) def restrict(id, bit): # restrict to bit 4-6 if 6 < int(bit) < 3: abort(403) return character_data = CharacterInfo.query.filter(CharacterInfo.id == id).first() if character_data == {}: abort(404) return log_audit(f"Updated ({character_data.id}){character_data.name}'s permission map to \ {character_data.permission_map ^ (1 << int(bit))} from {character_data.permission_map}") character_data.permission_map ^= (1 << int(bit)) character_data.save() return redirect(request.referrer if request.referrer else url_for("main.index")) @character_blueprint.route('/rescue/', methods=['GET', 'POST']) @login_required @gm_level(3) def rescue(id): form = RescueForm() character_data = CharacterXML.query.filter( CharacterXML.id == id ).first() character_xml = ET.XML(character_data.xml_data.replace("\"stt=", "\" stt=")) for zone in character_xml.findall('.//r'): if int(zone.attrib["w"]) % 100 == 0: form.save_world.choices.append( ( zone.attrib["w"], translate_from_locale(f"ZoneTable_{zone.attrib['w']}_DisplayDescription") ) ) if form.validate_on_submit(): new_zone = character_xml.find(f'.//r[@w="{form.save_world.data}"]') char = character_xml.find(".//char") char.attrib["lzx"] = new_zone.attrib["x"] char.attrib["lzy"] = new_zone.attrib["y"] char.attrib["lzz"] = new_zone.attrib["z"] char.attrib["lwid"] = form.save_world.data character_data.xml_data = ET.tostring(character_xml) character_data.save() return redirect(url_for('characters.view', id=id)) return render_template("character/rescue.html.j2", form=form) @character_blueprint.route('/upload/', methods=['GET', 'POST']) @login_required @gm_level(8) def upload(id): if not current_app.config["ENABLE_CHAR_XML_UPLOAD"]: flash("You must enable this setting to do this", "danger") return redirect(url_for('characters.view', id=id)) form = CharXMLUploadForm() character_data = CharacterXML.query.filter( CharacterXML.id == id ).first() if form.validate_on_submit(): character_data.xml_data = form.char_xml.data character_data.save() flash("You accept all consequences from these actions", "danger") log_audit(f"Updated {character_data.id}'s xml data") return redirect(url_for('characters.view', id=id)) form.char_xml.data = minidom.parseString(character_data.xml_data).toprettyxml(indent=" ") return render_template("character/upload.html.j2", form=form) @character_blueprint.route('/get/', methods=['GET']) @login_required @gm_level(3) def get(status): columns = [ ColumnDT(CharacterInfo.id), # 0 ColumnDT(Account.username), # 1 ColumnDT(CharacterInfo.name), # 2 ColumnDT(CharacterInfo.pending_name), # 3 ColumnDT(CharacterInfo.needs_rename), # 4 ColumnDT(CharacterInfo.last_login), # 5 ColumnDT(CharacterInfo.permission_map), # 6 ] query = None if status == "approved": query = db.session.query().select_from(CharacterInfo).join(Account).filter((CharacterInfo.pending_name == "") & (CharacterInfo.needs_rename == False)) elif status == "unapproved": query = db.session.query().select_from(CharacterInfo).join(Account).filter((CharacterInfo.pending_name != "") & (CharacterInfo.needs_rename == False)) else: query = db.session.query().select_from(CharacterInfo).join(Account) params = request.args.to_dict() rowTable = DataTables(params, query, columns) data = rowTable.output_result() for character in data["data"]: id = character["0"] character["0"] = f"""
{id}
View """ if not character["4"]: character["0"] += f""" Needs Rename """ if character["3"] or character["4"]: character["0"] += f""" Approve Name """ character["1"] = f""" View {character["1"]} """ if character["4"]: character["4"] = '''

''' else: character["4"] = '''

''' character["5"] = time.ctime(character["5"]) perm_map = character["6"] character["6"] = "" if perm_map & (1 << 4): character["6"] += "Restricted Trade
" if perm_map & (1 << 5): character["6"] += "Restricted Mail
" if perm_map & (1 << 6): character["6"] += "Restricted Chat
" return data