diff --git a/.gitignore b/.gitignore index 04b2c29..f8eb14e 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,5 @@ property_files/* *.log app/settings.py *.exe +*.csv +*.sql diff --git a/app/__init__.py b/app/__init__.py index 451b83e..46cef8a 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -18,7 +18,11 @@ from app.commands import ( load_property, gen_image_cache, gen_model_cache, - fix_clone_ids + fix_clone_ids, + parse_lucache, + makeup_unlisted_objects, + gen_new_locales, + xref_scripts ) from app.models import Account, AccountInvitation, AuditLog @@ -81,6 +85,10 @@ def create_app(): app.cli.add_command(gen_image_cache) app.cli.add_command(gen_model_cache) app.cli.add_command(fix_clone_ids) + app.cli.add_command(parse_lucache) + app.cli.add_command(makeup_unlisted_objects) + app.cli.add_command(gen_new_locales) + app.cli.add_command(xref_scripts) register_logging(app) register_settings(app) diff --git a/app/cdclient.py b/app/cdclient.py index 317c1cb..4e011fc 100644 --- a/app/cdclient.py +++ b/app/cdclient.py @@ -1408,58 +1408,89 @@ class ComponentsRegistry(db.Model): nullable=False ) - component = generic_relationship(component_type, component_id) + # component = generic_relationship(component_type, component_id) # From DLU source class ComponentType(enum.IntEnum): - COMPONENT_TYPE_CONTROLLABLE_PHYSICS = 1 # The ControllablePhysics Component - COMPONENT_TYPE_RENDER = 2 # The Render Component - COMPONENT_TYPE_SIMPLE_PHYSICS = 3 # The SimplePhysics Component - COMPONENT_TYPE_CHARACTER = 4 # The Character Component - COMPONENT_TYPE_SCRIPT = 5 # The Script Component - COMPONENT_TYPE_BOUNCER = 6 # The Bouncer Component - COMPONENT_TYPE_BUFF = 7 # The Buff Component - COMPONENT_TYPE_SKILL = 9 # The Skill Component - COMPONENT_TYPE_ITEM = 11 # The Item Component - COMPONENT_TYPE_VENDOR = 16 # The Vendor Component - COMPONENT_TYPE_INVENTORY = 17 # The Inventory Component - COMPONENT_TYPE_SHOOTING_GALLERY = 19 # The Shooting Gallery Component - COMPONENT_TYPE_RIGID_BODY_PHANTOM_PHYSICS = 20 # The RigidBodyPhantomPhysics Component - COMPONENT_TYPE_COLLECTIBLE = 23 # The Collectible Component - COMPONENT_TYPE_MOVING_PLATFORM = 25 # The MovingPlatform Component - COMPONENT_TYPE_PET = 26 # The Pet Component - COMPONENT_TYPE_VEHICLE_PHYSICS = 30 # The VehiclePhysics Component - COMPONENT_TYPE_MOVEMENT_AI = 31 # The MovementAI Component - COMPONENT_TYPE_PROPERTY = 36 # The Property Component - COMPONENT_TYPE_SCRIPTED_ACTIVITY = 39 # The ScriptedActivity Component - COMPONENT_TYPE_PHANTOM_PHYSICS = 40 # The PhantomPhysics Component - COMPONENT_TYPE_PROPERTY_ENTRANCE = 43 # The PhantomPhysics Component - COMPONENT_TYPE_PROPERTY_MANAGEMENT = 45 # The PropertyManagement Component - COMPONENT_TYPE_REBUILD = 48 # The Rebuild Component - COMPONENT_TYPE_SWITCH = 49 # The Switch Component - COMPONENT_TYPE_ZONE_CONTROL = 50 # The ZoneControl Component - COMPONENT_TYPE_PACKAGE = 53 # The Package Component - COMPONENT_TYPE_PLAYER_FLAG = 58 # The PlayerFlag Component - COMPONENT_TYPE_BASE_COMBAT_AI = 60 # The BaseCombatAI Component - COMPONENT_TYPE_MODULE_ASSEMBLY = 61 # The ModuleAssembly Component - COMPONENT_TYPE_PROPERTY_VENDOR = 65 # The PropertyVendor Component - COMPONENT_TYPE_ROCKET_LAUNCH = 67 # The RocketLaunch Component - COMPONENT_TYPE_RACING_CONTROL = 71 # The RacingControl Component - COMPONENT_TYPE_MISSION_OFFER = 73 # The MissionOffer Component - COMPONENT_TYPE_EXHIBIT = 75 # The Exhibit Component - COMPONENT_TYPE_RACING_STATS = 74 # The Exhibit Component - COMPONENT_TYPE_SOUND_TRIGGER = 77 # The Sound Trigger Component - COMPONENT_TYPE_PROXIMITY_MONITOR = 78 # The Proximity Monitor Component - COMPONENT_TYPE_MISSION = 84 # The Mission Component - COMPONENT_TYPE_ROCKET_LAUNCH_LUP = 97 # The LUP Launchpad Componen - COMPONENT_TYPE_RAIL_ACTIVATOR = 104 - COMPONENT_TYPE_POSSESSOR = 107 # The Component 107 - COMPONENT_TYPE_POSSESSABLE = 108 # The Component 108 - COMPONENT_TYPE_BUILD_BORDER = 114 # The Build Border Component - COMPONENT_TYPE_DESTROYABLE = 1000 # The Destroyable Component + COMPONENT_TYPE_CONTROLLABLE_PHYSICS = 1 # noqa - The ControllablePhysics Component + COMPONENT_TYPE_RENDER = 2 # noqa - The Render Component + COMPONENT_TYPE_SIMPLE_PHYSICS = 3 # noqa - The SimplePhysics Component + COMPONENT_TYPE_CHARACTER = 4 # noqa - The Character Component + COMPONENT_TYPE_SCRIPT = 5 # noqa - The Script Component + COMPONENT_TYPE_BOUNCER = 6 # noqa - The Bouncer Component + COMPONENT_TYPE_BUFF = 7 # noqa - The Buff Component + COMPONENT_TYPE_SKILL = 9 # noqa - The Skill Component + COMPONENT_TYPE_ITEM = 11 # noqa - The Item Component + COMPONENT_TYPE_VENDOR = 16 # noqa - The Vendor Component + COMPONENT_TYPE_INVENTORY = 17 # noqa - The Inventory Component + COMPONENT_TYPE_SHOOTING_GALLERY = 19 # noqa - The Shooting Gallery Component + COMPONENT_TYPE_RIGID_BODY_PHANTOM_PHYSICS = 20 # noqa - The RigidBodyPhantomPhysics Component + COMPONENT_TYPE_COLLECTIBLE = 23 # noqa - The Collectible Component + COMPONENT_TYPE_MOVING_PLATFORM = 25 # noqa - The MovingPlatform Component + COMPONENT_TYPE_PET = 26 # noqa - The Pet Component + COMPONENT_TYPE_VEHICLE_PHYSICS = 30 # noqa - The VehiclePhysics Component + COMPONENT_TYPE_MOVEMENT_AI = 31 # noqa - The MovementAI Component + COMPONENT_TYPE_PROPERTY = 36 # noqa - The Property Component + COMPONENT_TYPE_SCRIPTED_ACTIVITY = 39 # noqa - The ScriptedActivity Component + COMPONENT_TYPE_PHANTOM_PHYSICS = 40 # noqa - The PhantomPhysics Component + COMPONENT_TYPE_PROPERTY_ENTRANCE = 43 # noqa - The PhantomPhysics Component + COMPONENT_TYPE_PROPERTY_MANAGEMENT = 45 # noqa - The PropertyManagement Component + COMPONENT_TYPE_REBUILD = 48 # noqa - The Rebuild Component + COMPONENT_TYPE_SWITCH = 49 # noqa - The Switch Component + COMPONENT_TYPE_ZONE_CONTROL = 50 # noqa - The ZoneControl Component + COMPONENT_TYPE_PACKAGE = 53 # noqa - The Package Component + COMPONENT_TYPE_PLAYER_FLAG = 58 # noqa - The PlayerFlag Component + COMPONENT_TYPE_BASE_COMBAT_AI = 60 # noqa - The BaseCombatAI Component + COMPONENT_TYPE_MODULE_ASSEMBLY = 61 # noqa - The ModuleAssembly Component + COMPONENT_TYPE_PROPERTY_VENDOR = 65 # noqa - The PropertyVendor Component + COMPONENT_TYPE_ROCKET_LAUNCH = 67 # noqa - The RocketLaunch Component + COMPONENT_TYPE_RACING_CONTROL = 71 # noqa - The RacingControl Component + COMPONENT_TYPE_MISSION_OFFER = 73 # noqa - The MissionOffer Component + COMPONENT_TYPE_EXHIBIT = 75 # noqa - The Exhibit Component + COMPONENT_TYPE_RACING_STATS = 74 # noqa - The Exhibit Component + COMPONENT_TYPE_SOUND_TRIGGER = 77 # noqa - The Sound Trigger Component + COMPONENT_TYPE_PROXIMITY_MONITOR = 78 # noqa - The Proximity Monitor Component + COMPONENT_TYPE_MISSION = 84 # noqa - The Mission Component + COMPONENT_TYPE_ROCKET_LAUNCH_LUP = 97 # noqa - The LUP Launchpad Componen + COMPONENT_TYPE_RAIL_ACTIVATOR = 104 # noqa + COMPONENT_TYPE_POSSESSOR = 107 # noqa - The Component 107 + COMPONENT_TYPE_POSSESSABLE = 108 # noqa - The Component 108 + COMPONENT_TYPE_BUILD_BORDER = 114 # noqa - The Build Border Component + COMPONENT_TYPE_DESTROYABLE = 1000 # noqa - The Destroyable Component +# class ComponentMap(enum.Enum): +# 1 = ["PhysicsComponent"] +# 2 = ["RenderComponent"] +# 3 = ["PhysicsComponent"] +# 5 = ["ScriptComponent"] +# 7 = ["DestructibleComponent"] +# 9 = ["ObjectSkills", "SkillBehavior"] +# 12 = ["RebuildComponent"] +# 15 = ["RenderComponent"] +# 17 = ["InventoryComponent"] +# 18 = ["PhysicsComponent"] +# 20 = ["PhysicsComponent"] +# 24 = ["Blueprints"] +# 26 = ["PetComponent"] +# 29 = ["JetPackPadComponent"] +# 30 = ["VehiclePhysics"] +# 31 = ["MovementAIComponent"] +# 33 = ["mapIcon", "NpcIcons"] +# 40 = ["PhysicsComponent"] +# 46 = ["PhysicsComponent"] +# 47 = ["PhysicsComponent"] +# 48 = ["RebuildComponent"] +# 52 = ["ChoiceBuildComponent"] +# 53 = ["PackageComponent"] +# 60 = ["BaseCombatAIComponent"] +# 61 = ["ModularBuildComponent"] +# 67 = ["RocketLaunchpadControlComponent"] +# 73 = ["MissionNPCComponent"] +# 75 = ["LUPExhibitComponent"] +# 78 = ["ProximityMonitor"] + class ControlSchemes(db.Model): __tablename__ = 'ControlSchemes' __bind_key__ = 'cdclient' diff --git a/app/commands.py b/app/commands.py index aaa2e65..7a7ec84 100644 --- a/app/commands.py +++ b/app/commands.py @@ -15,7 +15,17 @@ from multiprocessing import Pool from functools import partial from sqlalchemy import func import time +import csv +import json +from app.cdclient import ( + ComponentsRegistry, + RenderComponent, + ItemComponent, + Objects, + ScriptComponent, +) +from app.luclient import translate_from_locale @click.command("init_db") @click.argument('drop_tables', nargs=1) @@ -180,6 +190,187 @@ def load_property(zone, player): ) new_prop_content.save() +@click.command("parse_lucache") +@with_appcontext +def parse_lucache(): + """Parses lucache csv file dump from nexus hq""" + unlisted_ids = [146, 147, 938, 1180, 1692, 1715, 1797, 1799, 1824, 1846, 1847, 1848, 1849, 1850, 1872, 1877, 1887, 1928, 1937, 1968, 1970, 1971, 1972, 1974, 1976, 1977, 1978, 1979, 1980, 1981, 1983, 1984, 2189, 2401, 2402, 2403, 2404, 2405, 2406, 2407, 2408, 2416, 2417, 2418, 2420, 2421, 2422, 2423, 2424, 2425, 2426, 2427, 2429, 2430, 2431, 2432, 2433, 2434, 2435, 2436, 2529, 2530, 2553, 2583, 2655, 2656, 2669, 2947, 2948, 3009, 3058, 3068, 3078, 3807, 3812, 3937, 4828, 4874, 4875, 4876, 4877, 4943, 4954, 5839, 5840, 6196, 6218, 6219, 6221, 6433, 6471, 6696, 6821, 6877, 6888, 6889, 6891, 6892, 6893, 6894, 6896, 6897, 6983, 7277, 7551, 7552, 7553, 7554, 7609, 7701, 7713, 7723, 7753, 7754, 7755, 7756, 7760, 7777, 7791, 7824, 7872, 8046, 8053, 8146, 9865, 9866, 9867, 9868, 10126, 10291, 10292, 10293, 10294, 10518, 10630, 10631, 10987, 11511, 11512, 11513, 11514, 11515, 11516, 11517, 11518, 11519, 11520, 11521, 11522, 11523, 11524, 11525, 12096, 12097, 12099, 12100, 12104, 12105, 12111, 12112, 12113, 12324, 12325, 12326, 12553, 12666, 12668, 12670, 12671, 12673, 12674, 12676, 12679, 12680, 12683, 12684, 12685, 12687, 12692, 12694, 12697, 12699, 12701, 12703, 12704, 12713, 12716, 12717, 12727, 12736, 12738, 12739, 12745, 12746, 12750, 12751, 12752, 12757, 12787, 12790, 12791, 12794, 12795, 12799, 12800, 12803, 12887, 12888, 12902, 12904, 12905, 12906, 12907, 12941, 13060, 13061, 13071, 13075, 13076, 13077, 13092, 13093, 13094, 13106, 13118, 13121, 13126, 13127, 13150, 13191, 13192, 13275, 13276, 13277, 13278, 13280, 13295, 13410, 13411, 13510, 13638, 13740, 13742, 13776, 13782, 13905, 13925, 13926, 13927, 13928, 13929, 13930, 13931, 13932, 13953, 13958, 13974, 13996, 13997, 13998, 13999, 14000, 14001, 14002, 14056, 14057, 14058, 14059, 14060, 14061, 14062, 14063, 14064, 14065, 14066, 14067, 14068, 14069, 14070, 14071, 14072, 14073, 14074, 14075, 14076, 14077, 14078, 14079, 14080, 14081, 14090, 14094, 14111, 14135, 14140, 14170, 14171, 14188, 14200, 14202, 14206, 14207, 14208, 14209, 14210, 14211, 14212, 14213, 14228, 14229, 14314, 14428, 14483, 14515, 14522, 14531, 14535, 14536, 14538, 14548, 14554, 14587, 14588, 14589, 14597, 14598, 14599, 14605, 14607, 14608, 14609, 14610, 14611, 14612, 14613, 14614, 14615, 14616, 14617, 14618, 14619, 14620, 14621, 14622, 14623, 14624, 14625, 14626, 14627, 14628, 14629, 14630, 14631, 14632, 14633, 14634, 14635, 14636, 14637, 14638, 14639, 14640, 14641, 14642, 14643, 14644, 14645, 14646, 14647, 14648, 14649, 14650, 14651, 14652, 14653, 14654, 14655, 14656, 14657, 14658, 14659, 14660, 14661, 14662, 14663, 14664, 14665, 14666, 14667, 14668, 14686, 14687, 14688, 14689, 14690, 14704, 14706, 14707, 14716, 14717, 14721, 14722, 14727, 14728, 14729, 14779, 14795, 14799, 14800, 14803, 14815, 14820, 14821, 14822, 14823, 14824, 14825, 14826, 14827, 14831, 14832, 14838, 14839, 15852, 15853, 15854, 15855, 15856, 15857, 15858, 15859, 15860, 15861, 15862, 15863, 15864, 15865, 15885, 15886, 15887, 15888, 15889, 15893, 15894, 15898, 15921, 15923, 15925, 15928, 15930, 15931, 15932, 15933, 15934, 15938, 15939, 15940, 15941, 15942, 15945, 15958, 15962, 15963, 15964, 15965, 15966, 15967, 15968, 15969, 15970, 15971, 15972, 15973, 15981, 15984, 15985, 15986, 15987, 15988, 15989, 15996, 15997, 15998, 15999, 16000, 16001, 16002, 16003, 16004, 16005, 16007, 16008, 16009, 16010, 16011, 16025, 16026, 16027, 16028, 16036, 16039, 16042, 16046, 16051, 16056, 16071, 16072, 16073, 16074, 16075, 16077, 16078, 16079, 16080, 16081, 16089, 16090, 16091, 16092, 16108, 16109, 16110, 16111, 16112, 16113, 16114, 16115, 16116, 16117, 16124, 16125, 16126, 16127, 16128, 16129, 16130, 16137, 16138, 16139, 16140, 16142, 16145, 16167, 16168, 16169, 16170, 16171, 16172, 16173, 16174, 16175, 16176, 16177, 16200, 16201, 16202, 16204, 16212, 16253, 16254, 16418, 16437, 16469, 16479, 16489, 16505, 16641, 16645, 16646, 16648, 16655, 16658, 16659, 16660, 16661, 16662, 16665, 16666, 16667, 16668, 16669, 16670, 16671, 16672, 16673, 16674, 16675, 16676, 16677, 16678, 16679, 16680, 16681, 16685, 16686, 16687, 16688, 16689, 16690, 16691, 16692, 16693, 16694, 16695, 16696, 16697, 16698, 16699, 16700, 16701, 16702, 16703, 16704, 16705, 16706, 16707, 16708, 16709, 16712, 16714, 16717, 16718, 16719, 16720, 16721, 16722, 16724, 16725, 16726, 16727, 16732, 16733, 16734, 16735] # noqa + with open("lucache.csv") as cache_file: + csv_reader = csv.reader(cache_file, delimiter=',') + line_count = 0 + for row in csv_reader: + if row[0] == "id": + continue + if int(row[0]) in unlisted_ids: + json_data = json.loads(row[2]) + components = ComponentsRegistry.query.filter(ComponentsRegistry.id == int(row[0])).all() + obj_type = "Environmental" + desc = json_data["Description"] + if desc in ["None", None, ""]: + desc = row[1] + nametag = 0 + npcTemplateID = "null" + for comp in components: + if comp.component_type == 7: # Item + obj_type = "Smashable" + if comp.component_type == 11: # Item + obj_type = "Loot" + if comp.component_type == 35: # minifig + obj_type = "NPC" + npcTemplateID = comp.component_id + nametag = 1 + if comp.component_type == 42: # b3 + obj_type = "Behavior" + if comp.component_type == 60: # base combat ai + obj_type = "Enemy" + if comp.component_type == 73: # mission giver + if obj_type != "NPC": + obj_type = "Structure" + desc = f"__MG__{desc}" + if "vendor" in row[1].lower(): + obj_type = "NPC" + print(f"""INSERT INTO "Objects" ("id","name","placeable","type","description","localize","npcTemplateID","displayName","interactionDistance","nametag","_internalNotes","locStatus","gate_version","HQ_valid") VALUES ("{row[0]}", "{row[1].replace("_", " ")}", 1, "{obj_type}", "{desc}", 1, {npcTemplateID} , "{json_data["DisplayName"]}", null , {nametag}, "Unlisted Object", 0, null, 1);""") + if obj_type in ["NPC", "Smashable", "Loot"]: + print(f""" + {row[1]} + TRASNSLATE UNLISTED + {row[1]} + + + {desc} + TRASNSLATE UNLISTED + {desc} + """) + # print(f'{row[0]}: {json_data["DisplayName"]}') + line_count += 1 + # print(f'Processed {line_count} lines.') + + +@click.command("makeup_unlisted_objects") +@with_appcontext +def makeup_unlisted_objects(): + objs_left = [] + for obj in objs_left: + obj_type = "Environmental" + nametag = 0 + name = "Name Missing" + desc = "null" + npcTemplateID = "null" + components = ComponentsRegistry.query.filter(ComponentsRegistry.id == obj).all() + for comp in components: + if comp.component_type == 2: # render + render = RenderComponent.query.filter(RenderComponent.id == comp.component_id).first() + if render is not None: + + if render.render_asset not in [None, ""]: + name = render.render_asset.replace("_", " ").split('\\')[-1].split('/')[-1].split('.')[0].lower() + if name == "Name Missing": + if render.icon_asset not in [None, ""]: + name = render.icon_asset.replace("_", " ").split('\\')[-1].split('/')[-1].split('.')[0].lower() + name = name.replace("env ", "").replace("obj ", "").replace("minifig accessory ", "").replace("", "").replace("mf ", "").replace("cre ", "") + # print(f"{obj}: {name} : {alt_name}") + obj_type = "Smashable" + # else: + # print(f"{obj}: No Render") + if comp.component_type == 7: # destroyable + obj_type = "Smashable" + if comp.component_type == 11: # Item + item = ItemComponent.query.filter(ItemComponent.id == comp.component_id).first() + if item.itemType == 24: + obj_type = "Mount" + else: + obj_type = "Loot" + if comp.component_type == 35: # minifig + obj_type = "NPC" + npcTemplateID = comp.component_id + nametag = 1 + if comp.component_type == 42: # b3 + obj_type = "Behavior" + if comp.component_type == 60: # base combat ai + obj_type = "Enemy" + if comp.component_type == 73: # mission giver + if obj_type != "NPC": + obj_type = "Structure" + desc = f"__MG__{name}" + # print(f"""INSERT INTO "Objects" ("id","name","placeable","type","description","localize","npcTemplateID","displayName","interactionDistance","nametag","_internalNotes","locStatus","gate_version","HQ_valid") VALUES ("{obj}", "{name}", 1, "{obj_type}", "{desc}", 1, {npcTemplateID} , "{name}", null , {nametag}, "Unlisted Object", 0, null, 1);""") + if name != "Name Missing" and obj_type in ["Mount"]: + print(f""" + {name} + TRASNSLATE UNLISTED + {name} + """) + + +@click.command("gen_new_locales") +@with_appcontext +def gen_new_locales(): + objects = Objects.query.order_by(Objects.id).all() + for obj in objects: + if obj.type == "Loot": + if obj.name not in ["Name Missing", None, "None"] and obj.name[:1] != "m": + name_to_trans = f"Object_{obj.id}_name" + name_transed = translate_from_locale(name_to_trans) + if name_to_trans == name_transed: + print(f""" + {obj.name} + TRASNSLATE OLD + {obj.name} + """) + if obj.description not in ["None", None, ""]: + description_to_trans = f"Object_{obj.id}_description" + description_transed = translate_from_locale(description_to_trans) + if description_to_trans == description_transed: + print(f""" + {obj.description} + TRASNSLATE OLD + {obj.description} + """) + + +@click.command("xref_scripts") +@with_appcontext +def xref_scripts(): + """cross refernce scripts dir with script component table""" + scripts = ScriptComponent.query.all() + base = 'app/luclient/res/' + server = 0 + server_total = 0 + client = 0 + client_total = 0 + server_used = 0 + client_used = 0 + used_total = 0 + disk_scripts = [path for path in pathlib.Path('app/luclient/res/scripts').rglob("*.lua") if path.is_file()] + + for script in scripts: + script_comps = ComponentsRegistry.query.filter(ComponentsRegistry.component_type == 5).filter(ComponentsRegistry.component_id == script.id).all() + if len(script_comps) > 0: + used_total += 1 + if script.client_script_name not in [None, ""]: + cleaned_name = script.client_script_name.replace('\\', '/').lower() + client_script = pathlib.Path(f"{base}{cleaned_name}") + client_total += 1 + if not client_script.is_file(): + print(f"Missing Server Script: {client_script.as_posix()}") + client += 1 + if len(script_comps) > 0: + client_used += 1 + if script.script_name not in [None, ""]: + cleaned_name = script.script_name.replace('\\', '/').lower() + server_script = pathlib.Path(f"{base}{cleaned_name}") + server_total += 1 + if not server_script.is_file(): + print(f"Missing Client Script: {server_script.as_posix()}") + server += 1 + if len(script_comps) > 0: + server_used += 1 + + print(f"Missing {server}/{server_total} server scripts") + print(f"Missing {client}/{client_total} client scripts") + print(f"Missing {server_used}/{used_total} used server scripts") + print(f"Missing {client_used}/{used_total} used client scripts") + print(f"Total cdclient scripts {server_total + client_total}\nTotal disk scripts {len(disk_scripts)}") + @click.command("gen_image_cache") def gen_image_cache(): diff --git a/app/settings_example.py b/app/settings_example.py index 49c0688..670a1c4 100644 --- a/app/settings_example.py +++ b/app/settings_example.py @@ -7,6 +7,11 @@ APP_SYSTEM_ERROR_SUBJECT_LINE = APP_NAME + " system error" APP_SECRET_KEY = "" APP_DATABASE_URI = "mysql+pymysql://:@:/" +CONFIG_LINK = False +CONFIG_LINK_TITLE = "" +CONFIG_LINK_HREF = "" +CONFIG_LINK_TEXT = "" + # Send Analytics for Developers to better fix issues ALLOW_ANALYTICS = False diff --git a/app/templates/main/about.html.j2 b/app/templates/main/about.html.j2 index 5a43881..2011d0c 100644 --- a/app/templates/main/about.html.j2 +++ b/app/templates/main/about.html.j2 @@ -24,7 +24,27 @@ {% endfor %} -
+ + + +
+
+

Links

+ + {% if config.CONFIG_LINK %} +
+
+ {{ config.CONFIG_LINK_TITLE }} +
+ +
+ {% endif %} + +
Source