From 39adc2171c61ac3a43d148f709d2cd3b365eb317 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Tue, 23 Jun 2026 19:24:37 +0200 Subject: [PATCH] Migration complete --- Api/Controller/MigrateController.php | 79 +++++++++++++++ Authentication/WordPress.php | 91 +++++++++++++++++ Helper/Migration.php | 86 ++++++++++++++++ Import/Importer/RHPZForums.php | 97 +++++++++++++++++++ Listener.php | 50 ++++++++++ _output/code_event_listeners/_metadata.json | 6 ++ ...page_c6149e27cfdc21a20c8047e0e5cd5503.json | 9 ++ ...sses_64d8f27f58ba02af52958c1d4f94f812.json | 9 ++ _output/navigation/_metadata.json | 5 +- _output/navigation/rom_checker.json | 13 --- _output/navigation/rom_hasher.json | 2 +- _output/option_hint.php | 1 + _output/options/_metadata.json | 3 + _output/options/rhpz_enable_migration.json | 13 +++ _output/phrases/_metadata.json | 24 +++-- _output/phrases/entries.txt | 1 + _output/phrases/nav.rom_checker.txt | 1 - .../phrases/option.rhpz_enable_migration.txt | 1 + .../option_explain.rhpz_enable_migration.txt | 1 + _output/routes/_metadata.json | 3 + _output/routes/api_migrate_.json | 11 +++ _output/template_modifications/_metadata.json | 9 ++ ..._macros_member_stat_pairs_entry_count.json | 9 ++ .../rhpz_member_view_add_entry_tab.json | 9 ++ .../rhpz_member_view_add_entry_tabpanel.json | 9 ++ 25 files changed, 517 insertions(+), 25 deletions(-) create mode 100644 Api/Controller/MigrateController.php create mode 100644 Authentication/WordPress.php create mode 100644 Helper/Migration.php create mode 100644 Import/Importer/RHPZForums.php create mode 100644 _output/code_event_listeners/app_pub_render_page_c6149e27cfdc21a20c8047e0e5cd5503.json create mode 100644 _output/code_event_listeners/import_importer_classes_64d8f27f58ba02af52958c1d4f94f812.json delete mode 100644 _output/navigation/rom_checker.json create mode 100644 _output/options/rhpz_enable_migration.json create mode 100644 _output/phrases/entries.txt delete mode 100644 _output/phrases/nav.rom_checker.txt create mode 100644 _output/phrases/option.rhpz_enable_migration.txt create mode 100644 _output/phrases/option_explain.rhpz_enable_migration.txt create mode 100644 _output/routes/api_migrate_.json create mode 100644 _output/template_modifications/public/rhpz_member_macros_member_stat_pairs_entry_count.json create mode 100644 _output/template_modifications/public/rhpz_member_view_add_entry_tab.json create mode 100644 _output/template_modifications/public/rhpz_member_view_add_entry_tabpanel.json diff --git a/Api/Controller/MigrateController.php b/Api/Controller/MigrateController.php new file mode 100644 index 0000000..435eb2a --- /dev/null +++ b/Api/Controller/MigrateController.php @@ -0,0 +1,79 @@ +rhpz_enable_migration; + if( !$rhpz_enable_migration ){ + $this->error("Migration not enabled"); + } + + } + + public function actionPostUser(): AbstractReply + { + $input = $this->filter([ + 'username' => 'str', + 'email' => 'str', + 'profile[about]' => 'str', + 'profile[website]' => 'str', + 'register_date' => 'uint', + 'user_group_id' => 'uint', + 'xf_user_id' => 'uint', + 'avatar_path' => 'str', + 'banner_path' => 'str', + 'source_real_password' => 'str', + 'wp_password' => 'str', + 'xf_scheme_class' => 'str', + 'xf_password_data' => 'str', + ]); + + $this->assertAdminPermission('user'); + $this->assertRequiredApiInput(['username','email','user_group_id']); + + /** @var UserRepository $userRepository */ + $userRepository = \XF::repository(UserRepository::class); + $user = $userRepository->setupBaseUser(null); + + $userPlugin = $this->plugin(UserPlugin::class); + $userPlugin->userSaveProcessAdmin($user)->run(); + + /** @var UserAuth $authUser */ + $authUser = $user->getExistingRelation('Auth'); + if( $input['source_real_password'] === 'wp' ){ + $authUser->scheme_class = 'RomhackPlaza\Master:WordPress'; + $authUser->data = ['hash' => $input['wp_password']]; + $authUser->save(); + } elseif( $input['source_real_password'] === 'xf' ){ + $authUser->scheme_class = $input['xf_scheme_class']; + $authUser->data = @unserialize($input['xf_password_data']); + $authUser->save(); + } + + if( $input['avatar_path'] !== "" ){ + Migration::setAvatarFromPath($input['avatar_path'], $user); + } + if( $input['banner_path'] !== "" ){ + Migration::setBannerFromPath($input['banner_path'], $user); + } + if( $input['register_date'] != 0 ){ + $user->register_date = $input['register_date']; + $user->save(); + } + + return $this->apiSuccess(['success' => true, 'user_id' => $user->user_id, 'password_set' => true ]); + + } +} diff --git a/Authentication/WordPress.php b/Authentication/WordPress.php new file mode 100644 index 0000000..6616504 --- /dev/null +++ b/Authentication/WordPress.php @@ -0,0 +1,91 @@ +data) ) { + return false; + } + + return $this->verifyPassword($password, $this->data['hash']); + + } + + private function verifyPassword($password, $storedHash): bool + { + if( str_starts_with($storedHash, '$wp') ){ + $passwordToVerify = base64_encode( hash_hmac( 'sha384', $password, 'wp-sha384', true ) ); + return password_verify( $passwordToVerify, substr($storedHash, 3) ); + } + + if (str_starts_with($storedHash, '$2y$') || str_starts_with($storedHash, '$2a$') || str_starts_with($storedHash, '$2b$')) { + return password_verify($password, $storedHash); + } + + if (strlen($storedHash) === 34 && (str_starts_with($storedHash, '$P$') || str_starts_with($storedHash, '$H$'))) { + return $this->verifyPhpass($password, $storedHash); + } + + if (strlen($storedHash) <= 32) { + return hash_equals($storedHash, md5($password)); + } + + return false; + } + + private function verifyPhpass(string $password, string $storedHash): bool + { + $itoa64 = self::ITOA64; + $countLog2 = strpos($itoa64, $storedHash[3]); + if ($countLog2 < 7 || $countLog2 > 30) return false; + + $count = 1 << $countLog2; + $salt = substr($storedHash, 4, 8); + if (strlen($salt) !== 8) return false; + + $hash = md5($salt . $password, true); + do { + $hash = md5($hash . $password, true); + } while (--$count); + + $output = substr($storedHash, 0, 12) . self::encode64($hash, $itoa64); + return hash_equals($storedHash, $output); + } + + private function encode64(string $input, string $itoa64): string + { + $output = ''; + $i = 0; + $count = strlen($input); + do { + $value = ord($input[$i++]); + $output .= $itoa64[$value & 0x3f]; + if ($i < $count) $value |= ord($input[$i]) << 8; + $output .= $itoa64[($value >> 6) & 0x3f]; + if ($i++ >= $count) break; + if ($i < $count) $value |= ord($input[$i]) << 16; + $output .= $itoa64[($value >> 12) & 0x3f]; + if ($i++ >= $count) break; + $output .= $itoa64[($value >> 18) & 0x3f]; + } while ($i < $count); + return $output; + } + + public function getAuthenticationName() + { + return 'RomhackPlaza\Master:WordPress'; + } +} \ No newline at end of file diff --git a/Helper/Migration.php b/Helper/Migration.php new file mode 100644 index 0000000..f8963f1 --- /dev/null +++ b/Helper/Migration.php @@ -0,0 +1,86 @@ +container(); + if( !isset( $container['oldXfDb'] ) ){ + $container['oldXfDb'] = function(Container $c){ + $config = \XF::config('oldxfdb'); + if( !$config ) + return null; + + return new \XF\Db\Mysqli\Adapter($config, true); + }; + } + + return $container['oldXfDb']; + } + + public static function uniqueUsername(string $username): string + { + $u = $username; + $i = 1; + while(\XF::em()->findOne('XF:User', ['username' => $u])){ + $u = $username . '_' . $i++; + } + + return $u; + } + + public static function copyUserPassword(int $xf_user_id, User $user): bool + { + $oldDb = self::oldXfDb(); + if( !$oldDb ){ + return false; + } + + $row = $oldDb->fetchRow( + 'SELECT scheme_class, data FROM xf_user_authenticate WHERE user_id = ?', + [$xf_user_id] + ); + if( !$row ){ + return false; + } + + $user->Auth->scheme_class = $row['scheme_class']; + $user->Auth->data = $row['data']; + return $user->Auth->save(); + } + + public static function setAvatarFromPath(string $avatar_path, User $user): bool + { + if(!is_readable($avatar_path)) + return false; + + $avatarService = \XF::service(AvatarService::class, $user); + + if (!$avatarService->setImage($avatar_path)) + return false; + + return $avatarService->updateAvatar(); + } + + public static function setBannerFromPath(mixed $banner_path, User $user) + { + if( !is_readable($banner_path) ) + return false; + + $bannerService = \XF::service(ProfileBannerService::class, $user); + if( !$bannerService->setImage($banner_path) ) + return false; + + return $bannerService->updateBanner(); + } + + +} \ No newline at end of file diff --git a/Import/Importer/RHPZForums.php b/Import/Importer/RHPZForums.php new file mode 100644 index 0000000..f0d2282 --- /dev/null +++ b/Import/Importer/RHPZForums.php @@ -0,0 +1,97 @@ + 'XenForo', + 'source' => 'RHPZ Forums (XF)' + ]; + } + + public function stepUserGroups(StepState $state) + { + $map = $this->getMigrationSetting('old_xf_group_to_xf_group'); + foreach( $map as $oldId => $newId ){ + $this->log('user_group', $oldId, $newId); + $state->imported++; + } + + return $state->complete(); + } + + public function stepUserFields(StepState $state) + { + return $state->complete(); + } + + public function stepUsers(StepState $state, array $stepConfig, $maxTime) + { + $rows = $this->sourceDb->fetchAll("SELECT user_id FROM xf_user ORDER BY user_id" ); + + foreach ( $rows as $row ) { + $oldId = $row['user_id']; + $newId = $this->getNewUserId($oldId); + + if( $newId ){ + $this->log('user', $oldId, $newId); + $state->imported++; + } + } + + return $state->complete(); + } + + public function stepAvatars(StepState $state, array $stepConfig, $maxTime) + { + return $state->complete(); + } + + public function stepNodes(StepState $state) + { + $map = $this->getMigrationSetting('old_xf_node_to_new_xf_node'); + foreach( $map as $oldId => $newId ){ + $this->log('node', $oldId, $newId); + $state->imported++; + } + + return $state->complete(); + } + + public function stepNodePermissions(StepState $state) + { + return $state->complete(); + } + + public function stepModerators(StepState $state) + { + return $state->complete(); + } + + private function getMigrationSetting(string $key): mixed + { + $db = Laravel::db(); + if( !$db ) + return null; + + $row = $db->fetchOne("SELECT value FROM migration_settings WHERE `key` = ? ", [$key]); + return $row ? json_decode( $row, true ) : []; + } + + private function getNewUserId(int $oldId): ?int + { + $db = Laravel::db(); + if( !$db ) + return null; + + $row = $db->fetchOne("SELECT user_id FROM migration_user_plan WHERE xf_user_id = ?", [$oldId]); + return $row ? (int) $row : null; + } +} \ No newline at end of file diff --git a/Listener.php b/Listener.php index 0413895..f18a76a 100644 --- a/Listener.php +++ b/Listener.php @@ -2,8 +2,24 @@ namespace RomhackPlaza\Master; +use XF\Container; +use XF\Import\Manager; +use XF\Mvc\Renderer\AbstractRenderer; +use XF\Mvc\Reply\AbstractReply; +use XF\SubContainer\Import; + class Listener { + + public static function importImporterClasses(Import $container, Container $parentContainer, array &$importers) + { + if( \XF::isAddOnActive('XFI') ) + $importers = array_merge( + $importers, + Manager::getImporterShortNamesForType('RomhackPlaza/Master') + ); + } + public static function criteriaUser(string $rule, array $data, \XF\Entity\User $user, bool &$returnValue ){ switch( $rule ){ case 'rhpz_entry_count': @@ -20,4 +36,38 @@ class Listener break; } } + + /** + * Credits to Kirby for the guest part. + * @link https://xenforo.com/community/resources/style-variation-default.9504/ + * + * @param \XF\App $app + * @param array $params + * @param AbstractReply $reply + * @param AbstractRenderer $renderer + * + * @return void + * @throws \XF\PrintableException + */ + public static function checkStyleVariation( \XF\App $app, array &$params, AbstractReply $reply, AbstractRenderer $renderer ) + { + $user = \XF::visitor(); + if( $user->user_id !== 0 ){ // Logged in. + if( $user->style_variation !== 'default' && $user->style_variation !== 'alternate' ){ + $user->style_variation = 'default'; + $user->save(); + } + } else { + $cookie = $app->request()->getCookie('style_variation'); + if( $cookie !== 'default' && $cookie !== 'alternate' ){ + $app->response()->setCookie('style_variation', 'default', 86400 * 365); + } + if( $user->style_variation === '' ){ + $style = $app->templater()->getStyle(); + if( $style->isVariationsEnabled() ){ + $user->setAsSaved('style_variation', 'default'); + } + } + } + } } \ No newline at end of file diff --git a/_output/code_event_listeners/_metadata.json b/_output/code_event_listeners/_metadata.json index 765d1ea..c5e6508 100644 --- a/_output/code_event_listeners/_metadata.json +++ b/_output/code_event_listeners/_metadata.json @@ -1,5 +1,11 @@ { + "app_pub_render_page_c6149e27cfdc21a20c8047e0e5cd5503.json": { + "hash": "95a8be583623f99ff47eef23a658e459" + }, "criteria_user_0cfa5d0143e6477a6bc20b11e255e850.json": { "hash": "f29b40ef7a5b420b212848744fd9b7db" + }, + "import_importer_classes_64d8f27f58ba02af52958c1d4f94f812.json": { + "hash": "2ff08f9036890eb77d7bc1c7fc02fc4c" } } \ No newline at end of file diff --git a/_output/code_event_listeners/app_pub_render_page_c6149e27cfdc21a20c8047e0e5cd5503.json b/_output/code_event_listeners/app_pub_render_page_c6149e27cfdc21a20c8047e0e5cd5503.json new file mode 100644 index 0000000..dcb2a2c --- /dev/null +++ b/_output/code_event_listeners/app_pub_render_page_c6149e27cfdc21a20c8047e0e5cd5503.json @@ -0,0 +1,9 @@ +{ + "event_id": "app_pub_render_page", + "execute_order": 10, + "callback_class": "RomhackPlaza\\Master\\Listener", + "callback_method": "checkStyleVariation", + "active": true, + "hint": "", + "description": "" +} \ No newline at end of file diff --git a/_output/code_event_listeners/import_importer_classes_64d8f27f58ba02af52958c1d4f94f812.json b/_output/code_event_listeners/import_importer_classes_64d8f27f58ba02af52958c1d4f94f812.json new file mode 100644 index 0000000..7723f3c --- /dev/null +++ b/_output/code_event_listeners/import_importer_classes_64d8f27f58ba02af52958c1d4f94f812.json @@ -0,0 +1,9 @@ +{ + "event_id": "import_importer_classes", + "execute_order": 15, + "callback_class": "RomhackPlaza\\Master\\Listener", + "callback_method": "importImporterClasses", + "active": true, + "hint": "", + "description": "" +} \ No newline at end of file diff --git a/_output/navigation/_metadata.json b/_output/navigation/_metadata.json index 761cae1..445a2d9 100644 --- a/_output/navigation/_metadata.json +++ b/_output/navigation/_metadata.json @@ -41,11 +41,8 @@ "pages.json": { "hash": "8d75bf99b2fb6528f84433f4519d4da3" }, - "rom_checker.json": { - "hash": "c9349db6120520e82d02b793a30e67ce" - }, "rom_hasher.json": { - "hash": "e14613a1bd0f1e1fadc084a009801570" + "hash": "f322c39dd3ea03343ab626a1da796c55" }, "rom_patcher.json": { "hash": "dff75392eea1fdffdaec193913d5d8a0" diff --git a/_output/navigation/rom_checker.json b/_output/navigation/rom_checker.json deleted file mode 100644 index da65b88..0000000 --- a/_output/navigation/rom_checker.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "parent_navigation_id": "tools", - "display_order": 300, - "navigation_type_id": "basic", - "type_config": { - "link": "{$xf.options.homePageUrl}/tools/rom-checker", - "display_condition": "", - "extra_attributes": { - "icon": "check" - } - }, - "enabled": true -} \ No newline at end of file diff --git a/_output/navigation/rom_hasher.json b/_output/navigation/rom_hasher.json index cb7554f..3cd079c 100644 --- a/_output/navigation/rom_hasher.json +++ b/_output/navigation/rom_hasher.json @@ -3,7 +3,7 @@ "display_order": 200, "navigation_type_id": "basic", "type_config": { - "link": "{$xf.options.homePageUrl}/tools/rom-hasher", + "link": "{$xf.options.homePageUrl}/hash", "display_condition": "", "extra_attributes": { "icon": "hash" diff --git a/_output/option_hint.php b/_output/option_hint.php index 8fee43d..f3bb474 100644 --- a/_output/option_hint.php +++ b/_output/option_hint.php @@ -13,6 +13,7 @@ namespace XF; /** * @property non-negative-int|null $rhpz_club_node_id Club Parent Node ID * @property string|null $rhpz_delete_account_master_key Delete account Master key + * @property bool|null $rhpz_enable_migration Enable Migration functions */ class Options { diff --git a/_output/options/_metadata.json b/_output/options/_metadata.json index d4f4f29..24f0207 100644 --- a/_output/options/_metadata.json +++ b/_output/options/_metadata.json @@ -4,5 +4,8 @@ }, "rhpz_delete_account_master_key.json": { "hash": "d649af68f404568ba3c06cc0a7121649" + }, + "rhpz_enable_migration.json": { + "hash": "6a3f9a6223e06f02636e434bae26cc93" } } \ No newline at end of file diff --git a/_output/options/rhpz_enable_migration.json b/_output/options/rhpz_enable_migration.json new file mode 100644 index 0000000..549b4c9 --- /dev/null +++ b/_output/options/rhpz_enable_migration.json @@ -0,0 +1,13 @@ +{ + "edit_format": "onoff", + "edit_format_params": "", + "data_type": "boolean", + "sub_options": [], + "validation_class": "", + "validation_method": "", + "advanced": true, + "default_value": "0", + "relations": { + "romhackplaza": 500 + } +} \ No newline at end of file diff --git a/_output/phrases/_metadata.json b/_output/phrases/_metadata.json index 13925ba..301278a 100644 --- a/_output/phrases/_metadata.json +++ b/_output/phrases/_metadata.json @@ -23,6 +23,12 @@ "version_string": "1.0.0", "hash": "15d96c48f857b975c051d7c98b13a385" }, + "entries.txt": { + "global_cache": false, + "version_id": 1000000, + "version_string": "1.0.0", + "hash": "bc14b0a9c516310fc195a778937cc0a0" + }, "nav.about.txt": { "global_cache": false, "version_id": 1000000, @@ -107,12 +113,6 @@ "version_string": "1.0.0", "hash": "453aceb005ceaf54a47da15fee8b2a26" }, - "nav.rom_checker.txt": { - "global_cache": false, - "version_id": 1000000, - "version_string": "1.0.0", - "hash": "f5e8c49f0487c68231a8804df2379cc5" - }, "nav.rom_hasher.txt": { "global_cache": false, "version_id": 1000000, @@ -155,6 +155,12 @@ "version_string": "1.0.0", "hash": "8b275759f2215b3b242de29110f05a4c" }, + "option.rhpz_enable_migration.txt": { + "global_cache": false, + "version_id": 1000000, + "version_string": "1.0.0", + "hash": "5c647f124187d520df70e6c1610fb041" + }, "option_explain.rhpz_club_node_id.txt": { "global_cache": false, "version_id": 1000000, @@ -167,6 +173,12 @@ "version_string": "1.0.0", "hash": "d41d8cd98f00b204e9800998ecf8427e" }, + "option_explain.rhpz_enable_migration.txt": { + "global_cache": false, + "version_id": 1000000, + "version_string": "1.0.0", + "hash": "d98b161589c31532e68fe26daa70dc32" + }, "option_group.romhackplaza.txt": { "global_cache": false, "version_id": 1000000, diff --git a/_output/phrases/entries.txt b/_output/phrases/entries.txt new file mode 100644 index 0000000..771ee94 --- /dev/null +++ b/_output/phrases/entries.txt @@ -0,0 +1 @@ +Entries \ No newline at end of file diff --git a/_output/phrases/nav.rom_checker.txt b/_output/phrases/nav.rom_checker.txt deleted file mode 100644 index ffb5df8..0000000 --- a/_output/phrases/nav.rom_checker.txt +++ /dev/null @@ -1 +0,0 @@ -ROM Checker \ No newline at end of file diff --git a/_output/phrases/option.rhpz_enable_migration.txt b/_output/phrases/option.rhpz_enable_migration.txt new file mode 100644 index 0000000..fc35068 --- /dev/null +++ b/_output/phrases/option.rhpz_enable_migration.txt @@ -0,0 +1 @@ +Enable Migration functions \ No newline at end of file diff --git a/_output/phrases/option_explain.rhpz_enable_migration.txt b/_output/phrases/option_explain.rhpz_enable_migration.txt new file mode 100644 index 0000000..0f58791 --- /dev/null +++ b/_output/phrases/option_explain.rhpz_enable_migration.txt @@ -0,0 +1 @@ +Enable migration endpoints and other things. \ No newline at end of file diff --git a/_output/routes/_metadata.json b/_output/routes/_metadata.json index f24d27b..803415d 100644 --- a/_output/routes/_metadata.json +++ b/_output/routes/_metadata.json @@ -1,4 +1,7 @@ { + "api_migrate_.json": { + "hash": "df6fcab95396a546b48a38eb9561b68f" + }, "api_romhackplaza_entry_.json": { "hash": "5f1609f559980b44af09fd85c3b34a30" }, diff --git a/_output/routes/api_migrate_.json b/_output/routes/api_migrate_.json new file mode 100644 index 0000000..70006e4 --- /dev/null +++ b/_output/routes/api_migrate_.json @@ -0,0 +1,11 @@ +{ + "route_type": "api", + "route_prefix": "migrate", + "sub_name": "", + "format": "", + "build_class": "", + "build_method": "", + "controller": "RomhackPlaza\\Master:Migrate", + "context": "", + "action_prefix": "" +} \ No newline at end of file diff --git a/_output/template_modifications/_metadata.json b/_output/template_modifications/_metadata.json index 310ec3c..04696b7 100644 --- a/_output/template_modifications/_metadata.json +++ b/_output/template_modifications/_metadata.json @@ -22,5 +22,14 @@ }, "public/rhpz_forum_overview_wrapper_search_button.json": { "hash": "af109f16aca8a12c4f8496fc4431f4b8" + }, + "public/rhpz_member_macros_member_stat_pairs_entry_count.json": { + "hash": "3d744e57f81abeb7f2d8291404f1e5b3" + }, + "public/rhpz_member_view_add_entry_tab.json": { + "hash": "35151043dca0be7987a72ee8b1cffc6f" + }, + "public/rhpz_member_view_add_entry_tabpanel.json": { + "hash": "5fcc29178d06fc924bdb604734cc9c59" } } \ No newline at end of file diff --git a/_output/template_modifications/public/rhpz_member_macros_member_stat_pairs_entry_count.json b/_output/template_modifications/public/rhpz_member_macros_member_stat_pairs_entry_count.json new file mode 100644 index 0000000..9a5d02b --- /dev/null +++ b/_output/template_modifications/public/rhpz_member_macros_member_stat_pairs_entry_count.json @@ -0,0 +1,9 @@ +{ + "template": "member_macros", + "description": "Add RHPZ Entry count on member profile page", + "execution_order": 10, + "enabled": true, + "action": "str_replace", + "find": "", + "replace": "
\n\t
{{ phrase('entries') }}
\n\t
\n\t\t{$user.rhpz_entry_count|number}\n\t
\n
" +} \ No newline at end of file diff --git a/_output/template_modifications/public/rhpz_member_view_add_entry_tab.json b/_output/template_modifications/public/rhpz_member_view_add_entry_tab.json new file mode 100644 index 0000000..da13520 --- /dev/null +++ b/_output/template_modifications/public/rhpz_member_view_add_entry_tab.json @@ -0,0 +1,9 @@ +{ + "template": "member_view", + "description": "Add entry tab on members page", + "execution_order": 10, + "enabled": true, + "action": "str_replace", + "find": "", + "replace": "{{ phrase('entries') }}\n$0" +} \ No newline at end of file diff --git a/_output/template_modifications/public/rhpz_member_view_add_entry_tabpanel.json b/_output/template_modifications/public/rhpz_member_view_add_entry_tabpanel.json new file mode 100644 index 0000000..6653e31 --- /dev/null +++ b/_output/template_modifications/public/rhpz_member_view_add_entry_tabpanel.json @@ -0,0 +1,9 @@ +{ + "template": "member_view", + "description": "Add Entry tab panel", + "execution_order": 10, + "enabled": true, + "action": "str_replace", + "find": "", + "replace": "
  • \n\tGo to this page\n
  • \n$0" +} \ No newline at end of file