Skip to content

Instantly share code, notes, and snippets.

@brianteeman
Forked from alexgarel/fix_assets.py
Created June 17, 2019 23:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save brianteeman/b7a0cb2bf807597636c305e15528f856 to your computer and use it in GitHub Desktop.
Save brianteeman/b7a0cb2bf807597636c305e15528f856 to your computer and use it in GitHub Desktop.
Utility to fixe a not so broken joomla assets table. Provided all assets are there and parent_id is correct, it will recompute level, rgt and lft for all entries.
import argparse
import getpass
import sys
import mysql.connector
class AssetsFixer:
cols = ["id", "parent_id", "level", "lft", "rgt"]
def __init__(self, user, password, db, prefix, host="localhost"):
self.db = mysql.connector.connect(user=user, password=password, db=db, host=host)
self.prefix = prefix
def get_all(self, query, *params):
c = self.db.cursor()
c.execute(query, params)
return c.fetchall()
def collect_info(self):
by_id = self.by_id = {}
by_parent_id = self.by_parent_id = {}
query = "select %s from %sassets" % (", ".join(self.cols), self.prefix)
for values in self.get_all(query):
row = dict(zip(self.cols, values))
by_id[row["id"]] = row
siblings = by_parent_id.setdefault(row["parent_id"], [])
siblings.append(row["id"])
def fix_data(self, parent_id, level, lft):
"""depth first descent
"""
child_lft = lft
children = self.by_parent_id.get(parent_id, [])
# sort on actual lft
children.sort(key=lambda i: self.by_id[i]['lft'])
for child_id in children:
child_lft = self.fix_data(child_id, level + 1, child_lft + 1)
# fix row
data = self.by_id[parent_id]
data["level"] = level
data["lft"] = lft
data["rgt"] = child_lft + 1
return data["rgt"]
def update_db(self):
c = self.db.cursor()
update_cols = list(self.cols)
update_cols.remove("id")
update_expr = ", ".join("{col}=%({col})s".format(col=col) for col in update_cols)
update_query = "UPDATE {prefix}assets SET {update_expr} WHERE id=%(id)s".format(
prefix=self.prefix, update_expr=update_expr)
c.executemany(update_query, list(self.by_id.values()))
self.db.commit()
def __call__(self):
print("Collecting info")
self.collect_info()
print("Fixing")
roots = self.by_parent_id[0]
assert len(roots) == 1, "More than one root !"
root = roots[0]
self.fix_data(root, level=0, lft=1)
print("Updating db")
self.update_db()
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description=("Utility to fixe a not so broken joomla assets table. " +
"Provided parent is correct"))
parser.add_argument("-u", "--username", required=True)
parser.add_argument("-l", "--hostname", default="localhost")
parser.add_argument("database", nargs=1)
parser.add_argument("prefix", nargs=1, help="tables prefix")
opts = parser.parse_args()
answer = input(
"Note that this is a potentially dangerous operation, " +
"did you backup you database first ? (yes/no):")
if answer == "yes":
password = getpass.getpass()
af = AssetsFixer(
db=opts.database[0], prefix=opts.prefix[0], user=opts.username, password=password)
af()
else:
print("So backup first")
sys.exit(1)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment