function installExtension($manifest, $upgrade = false) { $bigtree["group_match"] = $bigtree["module_match"] = $bigtree["route_match"] = $bigtree["class_name_match"] = $bigtree["form_id_match"] = $bigtree["view_id_match"] = $bigtree["report_id_match"] = array(); $extension = sqlescape($manifest["id"]); // Turn off foreign key checks so we can reference the extension before creating it sqlquery("SET foreign_key_checks = 0"); // Upgrades drop existing modules, templates, etc -- we don't drop settings because they have user data if (is_array($upgrade)) { sqlquery("DELETE FROM bigtree_module_groups WHERE extension = '{$extension}'"); sqlquery("DELETE FROM bigtree_modules WHERE extension = '{$extension}'"); sqlquery("DELETE FROM bigtree_templates WHERE extension = '{$extension}'"); sqlquery("DELETE FROM bigtree_callouts WHERE extension = '{$extension}'"); sqlquery("DELETE FROM bigtree_field_types WHERE extension = '{$extension}'"); sqlquery("DELETE FROM bigtree_feeds WHERE extension = '{$extension}'"); // Import tables for new installs } else { foreach ($manifest["components"]["tables"] as $table_name => $sql_statement) { sqlquery("DROP TABLE IF EXISTS `{$table_name}`"); sqlquery($sql_statement); } } // Import module groups foreach ($manifest["components"]["module_groups"] as &$group) { if ($group) { $bigtree["group_match"][$group["id"]] = $this->createModuleGroup($group["name"]); // Update the group ID since we're going to save this manifest locally for uninstalling $group["id"] = $bigtree["group_match"][$group["id"]]; sqlquery("UPDATE bigtree_module_groups SET extension = '{$extension}' WHERE id = '" . $group["id"] . "'"); } } // Import modules foreach ($manifest["components"]["modules"] as &$module) { if ($module) { $group = $module["group"] && isset($bigtree["group_match"][$module["group"]]) ? $bigtree["group_match"][$module["group"]] : "NULL"; $gbp = sqlescape(is_array($module["gbp"]) ? BigTree::json($module["gbp"]) : $module["gbp"]); // Find a unique route $oroute = $route = $module["route"]; $x = 2; while (sqlrows(sqlquery("SELECT * FROM bigtree_modules WHERE route = '" . sqlescape($route) . "'"))) { $route = $oroute . "-{$x}"; $x++; } // Create the module sqlquery("INSERT INTO bigtree_modules (`name`,`route`,`class`,`icon`,`group`,`gbp`,`extension`) VALUES ('" . sqlescape($module["name"]) . "','" . sqlescape($route) . "','" . sqlescape($module["class"]) . "','" . sqlescape($module["icon"]) . "',{$group},'{$gbp}','{$extension}')"); $module_id = sqlid(); $bigtree["module_match"][$module["id"]] = $module_id; $bigtree["route_match"][$module["route"]] = $route; // Update the module ID since we're going to save this manifest locally for uninstalling $module["id"] = $module_id; // Create the embed forms foreach ($module["embed_forms"] as $form) { $this->createModuleEmbedForm($module_id, $form["title"], $form["table"], is_array($form["fields"]) ? $form["fields"] : json_decode($form["fields"], true), $form["hooks"], $form["default_position"], $form["default_pending"], $form["css"], $form["redirect_url"], $form["thank_you_message"]); } // Create views foreach ($module["views"] as $view) { $bigtree["view_id_match"][$view["id"]] = $this->createModuleView($module_id, $view["title"], $view["description"], $view["table"], $view["type"], is_array($view["options"]) ? $view["options"] : json_decode($view["options"], true), is_array($view["fields"]) ? $view["fields"] : json_decode($view["fields"], true), is_array($view["actions"]) ? $view["actions"] : json_decode($view["actions"], true), $view["suffix"], $view["preview_url"]); } // Create regular forms foreach ($module["forms"] as $form) { $bigtree["form_id_match"][$form["id"]] = $this->createModuleForm($module_id, $form["title"], $form["table"], is_array($form["fields"]) ? $form["fields"] : json_decode($form["fields"], true), $form["hooks"], $form["default_position"], $form["return_view"] ? $bigtree["view_id_match"][$form["return_view"]] : false, $form["return_url"], $form["tagging"]); } // Create reports foreach ($module["reports"] as $report) { $bigtree["report_id_match"][$report["id"]] = $this->createModuleReport($module_id, $report["title"], $report["table"], $report["type"], is_array($report["filters"]) ? $report["filters"] : json_decode($report["filters"], true), is_array($report["fields"]) ? $report["fields"] : json_decode($report["fields"], true), $report["parser"], $report["view"] ? $bigtree["view_id_match"][$report["view"]] : false); } // Create actions foreach ($module["actions"] as $action) { $this->createModuleAction($module_id, $action["name"], $action["route"], $action["in_nav"], $action["class"], $bigtree["form_id_match"][$action["form"]], $bigtree["view_id_match"][$action["view"]], $bigtree["report_id_match"][$action["report"]], $action["level"], $action["position"]); } } } // Import templates foreach ($manifest["components"]["templates"] as $template) { if ($template) { $resources = sqlescape(is_array($template["resources"]) ? BigTree::json($template["resources"]) : $template["resources"]); sqlquery("INSERT INTO bigtree_templates (`id`,`name`,`module`,`resources`,`level`,`routed`,`extension`) VALUES ('" . sqlescape($template["id"]) . "','" . sqlescape($template["name"]) . "','" . $bigtree["module_match"][$template["module"]] . "','{$resources}','" . sqlescape($template["level"]) . "','" . sqlescape($template["routed"]) . "','{$extension}')"); } } // Import callouts foreach ($manifest["components"]["callouts"] as $callout) { if ($callout) { $resources = sqlescape(is_array($callout["resources"]) ? BigTree::json($callout["resources"]) : $callout["resources"]); sqlquery("INSERT INTO bigtree_callouts (`id`,`name`,`description`,`display_default`,`display_field`,`resources`,`level`,`position`,`extension`) VALUES ('" . sqlescape($callout["id"]) . "','" . sqlescape($callout["name"]) . "','" . sqlescape($callout["description"]) . "','" . sqlescape($callout["display_default"]) . "','" . sqlescape($callout["display_field"]) . "','{$resources}','" . sqlescape($callout["level"]) . "','" . sqlescape($callout["position"]) . "','{$extension}')"); } } // Import Settings foreach ($manifest["components"]["settings"] as $setting) { if ($setting) { $this->createSetting($setting); sqlquery("UPDATE bigtree_settings SET extension = '{$extension}' WHERE id = '" . sqlescape($setting["id"]) . "'"); } } // Import Feeds foreach ($manifest["components"]["feeds"] as $feed) { if ($feed) { $fields = sqlescape(is_array($feed["fields"]) ? BigTree::json($feed["fields"]) : $feed["fields"]); $options = sqlescape(is_array($feed["options"]) ? BigTree::json($feed["options"]) : $feed["options"]); sqlquery("INSERT INTO bigtree_feeds (`route`,`name`,`description`,`type`,`table`,`fields`,`options`,`extension`) VALUES ('" . sqlescape($feed["route"]) . "','" . sqlescape($feed["name"]) . "','" . sqlescape($feed["description"]) . "','" . sqlescape($feed["type"]) . "','" . sqlescape($feed["table"]) . "','{$fields}','{$options}','{$extension}')"); } } // Import Field Types foreach ($manifest["components"]["field_types"] as $type) { if ($type) { $self_draw = $type["self_draw"] ? "'on'" : "NULL"; $use_cases = sqlescape(is_array($type["use_cases"]) ? json_encode($type["use_cases"]) : $type["use_cases"]); sqlquery("INSERT INTO bigtree_field_types (`id`,`name`,`use_cases`,`self_draw`,`extension`) VALUES ('" . sqlescape($type["id"]) . "','" . sqlescape($type["name"]) . "','{$use_cases}',{$self_draw},'{$extension}')"); } } // Upgrades don't drop tables, we run the SQL revisions instead if (is_array($upgrade)) { $old_revision = $upgrade["revision"]; $sql_revisions = $manifest["sql_revisions"]; // Go through all the SQL updates, we ksort first to ensure if the manifest somehow got out of order that we run the SQL update sequentially ksort($sql_revisions); foreach ($sql_revisions as $key => $statements) { if ($key > $old_revision) { foreach ($statements as $sql_statement) { sqlquery($sql_statement); } } } // Update the extension sqlquery("UPDATE bigtree_extensions SET name = '" . sqlescape($manifest["title"]) . "', version = '" . sqlescape($manifest["version"]) . "', last_updated = NOW(), manifest = '" . BigTree::json($manifest, true) . "' WHERE id = '" . sqlescape($manifest["id"]) . "'"); // Straight installs move files into place locally } else { // Make sure destination doesn't exist $destination_path = SERVER_ROOT . "extensions/" . $manifest["id"] . "/"; BigTree::deleteDirectory($destination_path); // Move the package to the extension directory rename(SERVER_ROOT . "cache/package/", $destination_path); BigTree::setDirectoryPermissions($destination_path); // Create the extension sqlquery("INSERT INTO bigtree_extensions (`id`,`type`,`name`,`version`,`last_updated`,`manifest`) VALUES ('" . sqlescape($manifest["id"]) . "','extension','" . sqlescape($manifest["title"]) . "','" . sqlescape($manifest["version"]) . "',NOW(),'" . BigTree::json($manifest, true) . "')"); } // Re-enable foreign key checks sqlquery("SET foreign_key_checks = 1"); // Empty view cache sqlquery("DELETE FROM bigtree_module_view_cache"); // Move public files into the site directory $public_dir = SERVER_ROOT . "extensions/" . $manifest["id"] . "/public/"; $site_contents = file_exists($public_dir) ? BigTree::directoryContents($public_dir) : array(); foreach ($site_contents as $file_path) { $destination_path = str_replace($public_dir, SITE_ROOT . "extensions/" . $manifest["id"] . "/", $file_path); BigTree::copyFile($file_path, $destination_path); } // Clear module class cache and field type cache. @unlink(SERVER_ROOT . "cache/bigtree-module-class-list.json"); @unlink(SERVER_ROOT . "cache/bigtree-form-field-types.json"); return $manifest; }