phpBB3.0.X匯入版面的Python指令碼
關聯的資料表
在phpBB3.0中匯入版面時, 需要處理的有兩張表, 一個是 forums, 一個是 acl_groups.
如果是乾淨的論壇, 可以不保留安裝時填入的預設分割槽和版面, 直接用以下語句初始化:
-- 清空 forums 表 TRUNCATE phpbb_forums; -- 清空 acl_groups 表 TRUNCATE phpbb3015.phpbb_acl_groups; -- 填入初始化許可權 INSERT INTO `phpbb_acl_groups` VALUES (1,0,85,0,1),(1,0,93,0,1),(1,0,111,0,1),(5,0,0,5,0),(5,0,0,1,0),(2,0,0,6,0),(3,0,0,6,0),(4,0,0,5,0),(4,0,0,10,0),(7,0,0,23,0);
如果是已經存在版面, 並且需要保留版面的論壇, 則僅需要記下當前的最大right_id
SELECT MAX(right_id) FROM phpbb_forums
.
需要的最小資料集
需要的最小欄位為 `forum_id`, `parent_id`, `left_id`, `right_id`, `forum_name`, `forum_type`
構造版面資料
phpBB3.0的版面為單個父節點的樹狀結構, 使用了parent_id, left_id, right_id 來標識版面間的層級關係以及排序順序. 在構造版面資料時, 需要的就是採集最小資料集, 並正確生成parent_id, left_id和right_id. 下面的例子使用的版面, 原資料是分割槽 + 版面的結構, 分割槽有層級關係, 版面有層級關係, 分割槽的ID與版面的ID有重疊, 並且分割槽與版面之間存在多個父節點的情況. 需要在生成中進行調整.
建立一個可用的ID序列, 用於將分割槽ID對映到可用ID序列上
數量不大的話, 這一步可以通過手工完成, 根據分割槽的數量, 觀察版面的ID序列, 列出可用的ID做成list
availableIds = [3, 8, 11, 12, 13, 27, 30, ...]
將分割槽加入版面列表
遍歷分割槽, 將舊ID對映到新ID上, 需要兩次遍歷, 第二次遍歷時構造父子關係, children變數用於在最後生成left_id和right_id
boardsDict = {} # The mapping between Id => board # Build the section map allSections = rbcommon.tb_section.find({}).sort('rank', 1) boards = [] # Record all boards topBoards = [] # the root board Ids sectionMap = {} # The mapping between old section Id => new board Id, for assigning new Ids for the sections cusor = 0 for section in allSections: sectionMap[str(section['_id'])] = availableIds[cusor] newId = availableIds[cusor] board = { 'oid': section['_id'], 'oldPid': section['parentId'], '_id': newId, 'name2': section['name2'], 'is_folder': 'true', 'desc': section['desc'], 'children': [] } boards.append(board) boardsDict[board['_id']] = board cusor += 1 for board in boards: if (board['oldPid'] != 0): board['parentId'] = sectionMap[str(board['oldPid'])] parent = boardsDict[board['parentId']] parent['children'].append(board['_id']) else: board['parentId'] = 0 topBoards.append(board['_id']) for board in boards: print('oid:{}, oldPid:{}, _id:{}, parentId:{}, children:{}'.format(board['oid'], board['oldPid'], board['_id'], board['parentId'], board['children']))
將版面加入列表
# Build the boards mongoBoards = rbcommon.tb_board.find({}) for mongoBoard in mongoBoards: board = { 'oid': mongoBoard['_id'], 'oldPid': 0, 'parentId': 0, '_id': mongoBoard['_id'], 'name2': mongoBoard['name2'], 'is_folder': mongoBoard['is_folder'], 'desc': mongoBoard['name'], 'children': [] } boards.append(board) if (board['_id'] in boardsDict.keys()): print('Error: {}'.format(board['_id'])) exit boardsDict[board['_id']] = board
完善版面層級關係
# Build the boards tree allSectionToBoards = rbcommon.tb_section_to_board.find({}) for s2b in allSectionToBoards: if (s2b['parentId'] == 0): # parent is section parentId = sectionMap[str(s2b['sectionId'])] parent = boardsDict[parentId] board = boardsDict[s2b['boardId']] # avoid the multiple parent if (board['parentId'] > 0): print('Duplicate {} for {}, board:{}'.format(parentId, board['parentId'], s2b['boardId'])) continue board['parentId'] = parentId parent['children'].append(s2b['boardId']) else: # parent is board parent = boardsDict[s2b['parentId']] board = boardsDict[s2b['boardId']] # avoid the multiple parent if (board['parentId'] > 0): print('Duplicate {} for {}, board:{}'.format(s2b['parentId'], board['parentId'], s2b['boardId'])) continue board['parentId'] = s2b['parentId'] parent['children'].append(s2b['boardId']) print("All boards:") for board in boards: print('oid:{}, oldPid:{}, _id:{}, parentId:{}, folder:{}, children:{}'.format( board['oid'], board['oldPid'], board['_id'], board['parentId'], board['is_folder'], board['children']))
使用遞迴填充left_id和right_id
其中counter的取值, 如果是乾淨的論壇並且前面已經執行了truncate, 就將counter設成1, 否則設成前面得到的right_id最大值 + 1. 這樣新匯入的分割槽和版面都會出現在原有分割槽和版面的下方
# Build the leftId and rightId markLeftAndRight(topBoards) print("Marked boards:") for board in boards: print('_id:{}, parentId:{}, left:{}, right:{}, folder:{}, children:{}'.format( board['_id'], board['parentId'], board['leftId'], board['rightId'], board['is_folder'], board['children'])) # 用於遞迴的方法 def markLeftAndRight(idList): global counter for id in idList: board = boardsDict[id] if ('leftId' in board): print('Error: {}'.format(id)) exit board['leftId'] = counter counter += 1 if (len(board['children']) > 0): markLeftAndRight(board['children']) board['rightId'] = counter counter += 1
.
寫入MySQL
用pymsql寫入mysql, 每寫入一個版面, 同時寫入對應的許可權, 注意分割槽和版面的預設許可權資料是不一樣的.
# Write it to MySQL for board in boards: try: with rbcommon.mysqlclient.cursor() as cursor: # insert forum sql = '''INSERT INTO `phpbb_forums` (`forum_id`, `parent_id`, `left_id`, `right_id`, `forum_parents`, `forum_name`, `forum_desc`, `forum_rules`, `forum_type`) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)''' cursor.execute(sql, ( board['_id'], board['parentId'], board['leftId'], board['rightId'], '', board['name2'], board['desc'], '', 0 if (board['is_folder'] == 'true') else 1)) rbcommon.mysqlclient.commit() # insert acl_group if (board['is_folder'] == 'true'): sql = 'INSERT INTO `phpbb_acl_groups` VALUES (1,%s,0,17,0),(2,%s,0,17,0),(3,%s,0,17,0),(6,%s,0,17,0)' cursor.execute(sql, ( board['_id'], board['_id'], board['_id'], board['_id'])) rbcommon.mysqlclient.commit() else: sql = 'INSERT INTO `phpbb_acl_groups` VALUES (1,%s,0,17,0),(2,%s,0,15,0),(3,%s,0,15,0),(4,%s,0,21,0),(5,%s,0,14,0),(5,%s,0,10,0),(6,%s,0,19,0),(7,%s,0,24,0)' cursor.execute(sql, ( board['_id'], board['_id'], board['_id'], board['_id'], board['_id'], board['_id'], board['_id'], board['_id'])) rbcommon.mysqlclient.commit() except Exception as e: print(e)
.
資料匯入後, 使用管理員帳號先在後臺清空快取, 再檢視和編輯版面