PageRenderTime 29ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

/bonfire/modules/builder/controllers/Developer.php

http://github.com/ci-bonfire/Bonfire
PHP | 770 lines | 471 code | 98 blank | 201 comment | 75 complexity | 9b6ffaa6234d57034512839c0f0ab696 MD5 | raw file
Possible License(s): LGPL-2.1
  1. <?php defined('BASEPATH') || exit('No direct script access allowed');
  2. /**
  3. * Bonfire
  4. *
  5. * An open source project to allow developers to jumpstart their development of
  6. * CodeIgniter applications
  7. *
  8. * @package Bonfire
  9. * @author Bonfire Dev Team
  10. * @copyright Copyright (c) 2011 - 2018, Bonfire Dev Team
  11. * @license http://opensource.org/licenses/MIT The MIT License
  12. * @link http://cibonfire.com
  13. * @since Version 1.0
  14. * @filesource
  15. */
  16. /**
  17. * Builder Developer Context Controller
  18. *
  19. * This controller displays the list of current modules in the
  20. * application/modules folder and also allows the user to create new modules and
  21. * contexts
  22. *
  23. * This code is originally based on Ollie Rattue's http://formigniter.org/ project
  24. *
  25. * @package Bonfire\Modules\Builder\Controllers\Developer
  26. * @author Bonfire Dev Team
  27. * @link http://cibonfire.com/docs/developer/builder
  28. */
  29. class Developer extends Admin_Controller
  30. {
  31. /** @var array The options from the /config/modulebuilder.php file. */
  32. private $options;
  33. //--------------------------------------------------------------------------
  34. /**
  35. * Setup restrictions and load configs, libraries and language files
  36. *
  37. * @return void
  38. */
  39. public function __construct()
  40. {
  41. parent::__construct();
  42. $this->auth->restrict('Site.Developer.View');
  43. $this->lang->load('builder');
  44. $this->load->config('modulebuilder');
  45. $this->options = $this->config->item('modulebuilder');
  46. if (isset($this->options['form_error_delimiters'])
  47. && is_array($this->options['form_error_delimiters'])
  48. && count($this->options['form_error_delimiters']) == 2
  49. ) {
  50. $this->form_validation->set_error_delimiters(
  51. $this->options['form_error_delimiters'][0],
  52. $this->options['form_error_delimiters'][1]
  53. );
  54. }
  55. Assets::add_module_css('builder', 'builder.css');
  56. Assets::add_module_js('builder', 'modulebuilder.js');
  57. Template::set_block('sub_nav', 'developer/_sub_nav');
  58. Template::set_block('sidebar', 'developer/sidebar');
  59. }
  60. /**
  61. * Display a list of installed modules
  62. *
  63. * Includes the options to create a new module or context and delete existing
  64. * modules.
  65. *
  66. * @return void
  67. */
  68. public function index()
  69. {
  70. $modules = Modules::list_modules(true);
  71. $configs = array();
  72. foreach ($modules as $module) {
  73. $configs[$module] = Modules::config($module);
  74. if (! isset($configs[$module]['name'])) {
  75. $configs[$module]['name'] = ucwords($module);
  76. } elseif (strpos($configs[$module]['name'], 'lang:') === 0) {
  77. // If the name is configured, check to see if it is a lang entry
  78. // and, if it is, pull it from the application_lang file.
  79. $configs[$module]['name'] = lang(str_replace('lang:', '', $configs[$module]['name']));
  80. }
  81. }
  82. // Sort the module list (by the name of each module's folder)
  83. ksort($configs);
  84. // Check that the modules folder is writable
  85. Template::set('writable', $this->checkWritable());
  86. Template::set('modules', $configs);
  87. Template::set('toolbar_title', lang('mb_toolbar_title_index'));
  88. Template::render('two_left');
  89. }
  90. //--------------------------------------------------------------------------
  91. // !Context Builder
  92. //--------------------------------------------------------------------------
  93. /**
  94. * Display the form which allows the user to create a context.
  95. *
  96. * @return void
  97. */
  98. public function create_context()
  99. {
  100. // Form submittal?
  101. if (isset($_POST['build'])) {
  102. $this->form_validation->set_rules('context_name', 'lang:mb_context_name', 'required|trim|alpha_numeric');
  103. if ($this->form_validation->run() !== false) {
  104. // Validated!
  105. $name = $this->input->post('context_name');
  106. $for_roles = $this->input->post('roles');
  107. $migrate = $this->input->post('migrate') == 'on';
  108. // Try to save the context, using the UI/Context helper
  109. $this->load->library('ui/contexts');
  110. if (Contexts::create_context($name, $for_roles, $migrate)) {
  111. Template::set_message(lang('mb_context_create_success'), 'success');
  112. redirect(SITE_AREA . '/developer/builder');
  113. }
  114. // Creating the context failed
  115. Template::set_message(lang('mb_context_create_error') . Contexts::errors(), 'error');
  116. }
  117. }
  118. // Load roles for display in the form.
  119. $this->load->model('roles/role_model');
  120. $this->role_model->select(
  121. array(
  122. 'role_id',
  123. 'role_name',
  124. )
  125. )
  126. ->where('deleted', 0);
  127. Template::set('roles', $this->role_model->find_all());
  128. Template::set('toolbar_title', lang('mb_create_a_context'));
  129. Template::render();
  130. }
  131. //--------------------------------------------------------------------------
  132. // !Module Builder
  133. //--------------------------------------------------------------------------
  134. /**
  135. * Display the form which allows the user to create a module.
  136. *
  137. * @return void
  138. */
  139. public function create_module($fields = 0)
  140. {
  141. $this->auth->restrict('Bonfire.Modules.Add');
  142. $hide_form = false;
  143. $this->field_total = $fields;
  144. // Validation failed
  145. if ($this->validate_form($this->field_total) == false) {
  146. // Display the modulebuilder_form
  147. $this->prepareModuleForm($this->field_total, isset($_POST['build']));
  148. } elseif ($this->input->post('module_db') == 'existing'
  149. && $this->field_total == 0
  150. ) {
  151. // Validation Passed, Use existing DB, need to detect the fields.
  152. //
  153. // If the table name includes the prefix, remove the prefix
  154. $_POST['table_name'] = preg_replace(
  155. "/^".$this->db->dbprefix."/",
  156. "",
  157. $this->input->post('table_name')
  158. );
  159. // Read the fields from the db table and pass them back to the form
  160. $table_fields = $this->table_info($this->input->post('table_name'));
  161. $num_fields = is_array($table_fields) ? count($table_fields) : 0;
  162. // $num_fields includes the primary key, field_total doesn't
  163. $fieldTotal = $num_fields > 0 ? $num_fields - 1 : $this->field_total;
  164. $formError = false;
  165. // If the table wasn't found, log/set an error message
  166. if (! empty($_POST) && $num_fields == 0) {
  167. $formError = true;
  168. $error_message = lang('mb_module_table_not_exist');
  169. log_message('error', "ModuleBuilder: {$error_message}");
  170. Template::set('error_message', $error_message);
  171. }
  172. Template::set('existing_table_fields', $table_fields);
  173. // Display the modulebuilder_form
  174. $this->prepareModuleForm($fieldTotal, $formError);
  175. } else {
  176. // Validation passed and ready to proceed
  177. $this->build_module($this->field_total);
  178. log_activity(
  179. $this->auth->user_id(),
  180. lang('mb_act_create') . ': ' . $this->input->post('module_name') . ' : ' . $this->input->ip_address(),
  181. 'modulebuilder'
  182. );
  183. Template::set_view('developer/output');
  184. }
  185. Template::set('error', array());
  186. Template::set('toolbar_title', lang('mb_toolbar_title_create'));
  187. Template::set('writable', $this->checkWritable());
  188. Template::render();
  189. }
  190. /**
  191. * Delete a module and all of its files.
  192. *
  193. * @return void
  194. */
  195. public function delete()
  196. {
  197. // If there's no module to delete, redirect
  198. $module_name = $this->input->post('module');
  199. if (empty($module_name)) {
  200. redirect(SITE_AREA . '/developer/builder');
  201. }
  202. $this->auth->restrict('Bonfire.Modules.Delete');
  203. // Remove the module's data and permissions from the database
  204. if ($this->deleteModuleData($module_name) === false) {
  205. // Something went wrong while trying to delete the data
  206. Template::set_message(lang('mb_delete_trans_false'), $this->db->error, 'error');
  207. redirect(SITE_AREA . '/developer/builder');
  208. }
  209. // Data deleted successfully, now try to remove the files.
  210. $this->load->helper('file');
  211. if (delete_files(Modules::path($module_name), true)) {
  212. @rmdir(Modules::path("{$module_name}/"));
  213. log_activity(
  214. $this->auth->user_id(),
  215. lang('mb_act_delete') . ": {$module_name} : " . $this->input->ip_address(),
  216. 'builder'
  217. );
  218. Template::set_message(lang('mb_delete_success'), 'success');
  219. } else {
  220. // Database removal succeeded, but the files may still be present
  221. Template::set_message(lang('mb_delete_success') . lang('mb_delete_success_db_only'), 'info');
  222. }
  223. redirect(SITE_AREA . '/developer/builder');
  224. }
  225. //--------------------------------------------------------------------------
  226. // !PRIVATE METHODS
  227. //--------------------------------------------------------------------------
  228. /**
  229. * Delete the data for a module
  230. *
  231. * Removes migration information, permissions based on the name of the
  232. * module, and any tables returned by the module's model's get_table()
  233. * method.
  234. *
  235. * @todo Use the migration library instead of doing everything directly.
  236. *
  237. * @param string $moduleName The name of the module
  238. *
  239. * @return bool true if the data is removed, false on error
  240. */
  241. private function deleteModuleData($moduleName)
  242. {
  243. $this->load->dbforge();
  244. $this->db->trans_begin();
  245. // Drop the migration record - old Migration schema method
  246. $moduleNameLower = preg_replace("/[ -]/", "_", strtolower($moduleName));
  247. if ($this->db->field_exists("{$moduleNameLower}_version", 'schema_version')) {
  248. $this->dbforge->drop_column('schema_version', "{$moduleNameLower}_version");
  249. }
  250. // Drop the Migration record - new Migration schema method
  251. if ($this->db->field_exists('version', 'schema_version')) {
  252. $this->db->delete('schema_version', array('type' => $moduleNameLower . '_'));
  253. }
  254. // Get any permission ids
  255. $this->load->model('permissions/permission_model');
  256. $permissionKey = $this->permission_model->get_key();
  257. $permissionIds = $this->permission_model->select($permissionKey)
  258. ->like('name', $moduleName . '.', 'after')
  259. ->find_all();
  260. // Undo any permissions that exist, from the roles as well
  261. if (! empty($permissionIds)) {
  262. foreach ($permissionIds as $row) {
  263. $this->permission_model->delete($row->permission_id);
  264. }
  265. }
  266. // Check whether there is a model to drop (a model should have a table
  267. // which may require dropping)
  268. $modelName = "{$moduleName}_model";
  269. if (Modules::file_path($moduleName, 'models', "{$modelName}.php")) {
  270. // Drop the table
  271. $this->load->model("{$moduleName}/{$modelName}", 'mt');
  272. $mtTableName = $this->mt->get_table();
  273. // If the model has a table and it exists in the database, drop it
  274. if (! empty($mtTableName) && $this->db->table_exists($mtTableName)) {
  275. $this->dbforge->drop_table($mtTableName);
  276. }
  277. }
  278. // Complete the database transaction or roll it back
  279. if ($this->db->trans_status() === false) {
  280. $this->db->trans_rollback();
  281. return false;
  282. }
  283. $this->db->trans_commit();
  284. return true;
  285. }
  286. /**
  287. * Prepare the variables used for the modulebuilder_form and set the view
  288. *
  289. * @todo Allow configuration of defaultRoleWithFullAccess
  290. *
  291. * @todo Ideally the field type variables would be set at a higher level for
  292. * consistent use throughout the builder
  293. *
  294. * @param int $fieldTotal The number of fields to add to the table
  295. * @param bool $formError Set to true if an error has occurred on the form
  296. *
  297. * @return void
  298. */
  299. private function prepareModuleForm($fieldTotal, $formError)
  300. {
  301. $this->load->model('roles/role_model');
  302. $this->role_model->select(array('role_id', 'role_name'))
  303. ->where('deleted', 0)
  304. ->order_by('role_name');
  305. $this->load->library('modulebuilder');
  306. $boolFieldTypes = array_merge($this->modulebuilder->getBooleanTypes(), array('BIT', 'BOOL', 'TINYINT'));
  307. $listFieldTypes = $this->modulebuilder->getListTypes();
  308. $textFieldTypes = $this->modulebuilder->getTextTypes();
  309. $dbFieldTypes = array();
  310. foreach (array_keys($this->modulebuilder->getDatabaseTypes()) as $key) {
  311. $dbFieldTypes[$key] = $key;
  312. }
  313. Template::set('availableContexts', config_item('contexts'));
  314. Template::set('boolFieldTypes', $boolFieldTypes);
  315. Template::set('db_field_types', $dbFieldTypes);
  316. Template::set('defaultRoleWithFullAccess', $this->auth->role_id());
  317. Template::set('field_numbers', range(0, 20));
  318. Template::set('field_total', $fieldTotal);
  319. Template::set('form_action_options', $this->options['form_action_options']);
  320. Template::set('form_error', $formError);
  321. Template::set('listFieldTypes', $listFieldTypes);
  322. Template::set('roles', $this->role_model->as_array()->find_all());
  323. Template::set(
  324. 'textarea_editors',
  325. array(
  326. '' => 'None',
  327. 'ckeditor' => 'CKEditor',
  328. 'elrte' => 'ElRte with ElFinder',
  329. 'markitup' => 'MarkitUp!',
  330. 'tinymce' => 'TinyMCE',
  331. 'xinha' => 'Xinha',
  332. )
  333. );
  334. Template::set('textFieldTypes', $textFieldTypes);
  335. Template::set(
  336. 'truefalse',
  337. array(
  338. 'false' => 'False',
  339. 'true' => 'True',
  340. )
  341. );
  342. Template::set('validation_limits', $this->options['validation_limits']);
  343. Template::set('validation_rules', $this->options['validation_rules']);
  344. Template::set(
  345. 'view_field_types',
  346. array(
  347. 'input' => 'INPUT',
  348. 'checkbox' => 'CHECKBOX',
  349. 'password' => 'PASSWORD',
  350. 'radio' => 'RADIO',
  351. 'select' => 'SELECT',
  352. 'textarea' => 'TEXTAREA',
  353. )
  354. );
  355. Template::set_view('developer/modulebuilder_form');
  356. }
  357. /**
  358. * Validate the modulebuilder form.
  359. *
  360. * @param int $field_total The number of fields to add to the table
  361. *
  362. * @return bool Whether the form data was valid or not
  363. */
  364. private function validate_form($field_total = 0)
  365. {
  366. $this->form_validation->set_rules("contexts_content", 'lang:mb_contexts_content', "trim|is_numeric");
  367. $this->form_validation->set_rules("contexts_developer", 'lang:mb_contexts_developer', "trim|is_numeric");
  368. $this->form_validation->set_rules("contexts_public", 'lang:mb_contexts_public', "trim|is_numeric");
  369. $this->form_validation->set_rules("contexts_reports", 'lang:mb_contexts_reports', "trim|is_numeric");
  370. $this->form_validation->set_rules("contexts_settings", 'lang:mb_contexts_settings', "trim|is_numeric");
  371. $this->form_validation->set_rules("module_db", 'lang:mb_module_db', "trim|alpha");
  372. $this->form_validation->set_rules("form_action_create", 'lang:mb_form_action_create', "trim|is_numeric");
  373. $this->form_validation->set_rules("form_action_delete", 'lang:mb_form_action_delete', "trim|is_numeric");
  374. $this->form_validation->set_rules("form_action_edit", 'lang:mb_form_action_edit', "trim|is_numeric");
  375. $this->form_validation->set_rules("form_action_view", 'lang:mb_form_action_view', "trim|is_numeric");
  376. $this->form_validation->set_rules("form_error_delimiters", 'lang:mb_form_err_delims', "required|trim");
  377. $this->form_validation->set_rules("module_description", 'lang:mb_form_mod_desc', "trim|required|addslashes");
  378. $this->form_validation->set_rules("module_name", 'lang:mb_form_mod_name', "trim|required|callback__modulename_check");
  379. $this->form_validation->set_rules("role_id", 'lang:mb_form_role_id', "trim|is_numeric");
  380. // If there's no database table, don't use the table validation
  381. if ($this->input->post('module_db')) {
  382. $this->form_validation->set_rules("table_name", 'lang:mb_form_table_name', "trim|required|alpha_dash");
  383. // If it's a new table, extra validation is required
  384. if ($this->input->post('module_db') == 'new') {
  385. $this->form_validation->set_rules("primary_key_field", 'lang:mb_form_primarykey', "required|trim|alpha_dash");
  386. $this->form_validation->set_rules("soft_delete_field", 'lang:mb_soft_delete_field', "trim|alpha_dash");
  387. $this->form_validation->set_rules("created_field", 'lang:mb_form_created_field', "trim|alpha_dash");
  388. $this->form_validation->set_rules("modified_field", 'lang:mb_form_modified_field', "trim|alpha_dash");
  389. $this->form_validation->set_rules('deleted_by_field', 'lang:mb_deleted_by_field', 'trim|alpha_dash');
  390. $this->form_validation->set_rules('created_by_field', 'lang:mb_form_created_by_field', 'trim|alpha_dash');
  391. $this->form_validation->set_rules('modified_by_field', 'lang:mb_form_modified_by_field', 'trim|alpha_dash');
  392. // textarea_editor seems to be gone...
  393. //$this->form_validation->set_rules("textarea_editor", 'lang:mb_form_text_ed', "trim|alpha_dash");
  394. } elseif ($this->input->post('module_db') == 'existing'
  395. && $field_total > 0
  396. ) {
  397. // If it's an existing table, the primary key validation is required
  398. $this->form_validation->set_rules("primary_key_field", 'lang:mb_form_primarykey', "required|trim|alpha_dash");
  399. }
  400. // No need to do any of the below on every iteration of the loop
  401. $lang_field_details = lang('mb_form_field_details') . ' ';
  402. $this->load->library('modulebuilder');
  403. // Make sure the length field is required if the DB Field type
  404. // requires a length
  405. $no_length = array_merge(
  406. $this->modulebuilder->getObjectTypes(),
  407. $this->modulebuilder->getBooleanTypes(),
  408. $this->modulebuilder->getDateTypes(),
  409. $this->modulebuilder->getTimeTypes()
  410. );
  411. $optional_length = array_diff(
  412. $this->modulebuilder->getIntegerTypes(),
  413. $this->modulebuilder->getBooleanTypes()
  414. );
  415. for ($counter = 1; $field_total >= $counter; $counter++) {
  416. $field_details_label = $lang_field_details . $counter . ' :: ';
  417. // We don't define the validation labels with 'lang:' in this
  418. // loop because we don't want to create language entries for
  419. // every possible $counter value
  420. // Better to do it this way round as this statement will be
  421. // fullfilled more than the one below
  422. if ($counter != 1) {
  423. $this->form_validation->set_rules("view_field_label$counter", $field_details_label . lang('mb_form_label'), 'trim|alpha_extra');
  424. } else {
  425. // At least one field is required in the form
  426. $this->form_validation->set_rules("view_field_label$counter", $field_details_label . lang('mb_form_label'), 'trim|required|alpha_extra');
  427. }
  428. $label = $this->input->post("view_field_label$counter");
  429. $name_required = empty($label) ? '' : 'required|';
  430. $this->form_validation->set_rules("view_field_name$counter", $field_details_label . lang('mb_form_fieldname'), "trim|{$name_required}callback__no_match[$counter]");
  431. $this->form_validation->set_rules("view_field_type$counter", $field_details_label . lang('mb_form_type'), "trim|required|alpha");
  432. $this->form_validation->set_rules("db_field_type$counter", $field_details_label . lang('mb_form_dbtype'), "trim|alpha");
  433. $field_type = $this->input->post("db_field_type$counter");
  434. $db_len_required = '';
  435. if (! empty($label)
  436. && ! in_array($field_type, $no_length)
  437. && ! in_array($field_type, $optional_length)
  438. ) {
  439. $db_len_required = '|required';
  440. }
  441. $this->form_validation->set_rules("db_field_length_value$counter", $field_details_label . lang('mb_form_length'), "trim{$db_len_required}");
  442. $this->form_validation->set_rules('validation_rules'.$counter.'[]', $field_details_label . lang('mb_form_rules'), 'trim');
  443. }
  444. }
  445. return $this->form_validation->run();
  446. }
  447. /**
  448. * Get the structure and details for the fields in the specified DB table
  449. *
  450. * @param string $table_name Name of the table to check
  451. *
  452. * @return mixed An array of fields or false if the table does not exist
  453. */
  454. private function table_info($table_name)
  455. {
  456. $newfields = array();
  457. // Check whether the table exists in this database
  458. if (! $this->db->table_exists($table_name)) {
  459. return false;
  460. }
  461. $fields = $this->db->field_data($table_name);
  462. // There may be something wrong or the database driver may not return
  463. // field data
  464. if (empty($fields)) {
  465. return false;
  466. }
  467. foreach ($fields as $field) {
  468. $max_length = null;
  469. $type = '';
  470. if (isset($field->type)) {
  471. if (strpos($field->type, "(")) {
  472. list($type, $max_length) = explode(
  473. "--",
  474. str_replace("(", "--", str_replace(")", "", $field->type))
  475. );
  476. } else {
  477. $type = $field->type;
  478. }
  479. }
  480. $values = '';
  481. if (isset($field->max_length)) {
  482. if (is_numeric($field->max_length)) {
  483. $max_length = $field->max_length;
  484. } else {
  485. $values = $field->max_length;
  486. }
  487. }
  488. $newfields[] = array(
  489. 'name' => isset($field->name) ? $field->name : '',
  490. 'type' => strtoupper($type),
  491. 'max_length' => $max_length == null ? '' : $max_length,
  492. 'values' => $values,
  493. 'primary_key' => isset($field->primary_key) && $field->primary_key == 1 ? 1 : 0,
  494. 'default' => isset($field->default) ? $field->default : null,
  495. );
  496. }
  497. return $newfields;
  498. }
  499. /**
  500. * Handles the heavy-lifting of building a module from ther user's specs.
  501. *
  502. * @param int $field_total The number of fields to add to the table
  503. *
  504. * @return void
  505. */
  506. private function build_module($field_total = 0)
  507. {
  508. $action_names = $this->input->post('form_action');
  509. $contexts = $this->input->post('contexts');
  510. $db_required = $this->input->post('module_db');
  511. $form_error_delimiters = explode(',', $this->input->post('form_error_delimiters'));
  512. $module_description = $this->input->post('module_description');
  513. $module_name = $this->input->post('module_name');
  514. $primary_key_field = $this->input->post('primary_key_field');
  515. $role_id = $this->input->post('role_id');
  516. $table_name = strtolower(preg_replace("/[ -]/", "_", $this->input->post('table_name')));
  517. $table_as_field_prefix = (bool) $this->input->post('table_as_field_prefix');
  518. $logUser = $this->input->post('log_user') == 1;
  519. $useCreated = $this->input->post('use_created') == 1;
  520. $useModified = $this->input->post('use_modified') == 1;
  521. $usePagination = $this->input->post('use_pagination') == 1;
  522. $useSoftDeletes = $this->input->post('use_soft_deletes') == 1;
  523. $created_field = $this->input->post('created_field') ?: 'created_on';
  524. $created_by_field = $this->input->post('created_by_field') ?: 'created_by';
  525. $soft_delete_field = $this->input->post('soft_delete_field') ?: 'deleted';
  526. $deleted_by_field = $this->input->post('deleted_by_field') ?: 'deleted_by';
  527. $modified_field = $this->input->post('modified_field') ?: 'modified_on';
  528. $modified_by_field = $this->input->post('modified_by_field') ?: 'modified_by';
  529. $textarea_editor = $this->input->post('textarea_editor');
  530. if ($primary_key_field == '') {
  531. $primary_key_field = $this->options['primary_key_field'];
  532. }
  533. if (! is_array($form_error_delimiters)
  534. || count($form_error_delimiters) != 2
  535. ) {
  536. $form_error_delimiters = $this->options['$form_error_delimiters'];
  537. }
  538. $controller_name = preg_replace("/[ -]/", "_", $module_name);
  539. $module_name_lower = strtolower($controller_name);
  540. $this->load->library('modulebuilder');
  541. $file_data = $this->modulebuilder->buildFiles(
  542. array(
  543. 'action_names' => $action_names,
  544. 'contexts' => $contexts,
  545. 'controller_name' => $controller_name,
  546. 'created_by_field' => $created_by_field,
  547. 'created_field' => $created_field,
  548. 'db_required' => $db_required,
  549. 'deleted_by_field' => $deleted_by_field,
  550. 'field_total' => $field_total,
  551. 'form_error_delimiters' => $form_error_delimiters,
  552. 'logUser' => $logUser,
  553. 'modified_by_field' => $modified_by_field,
  554. 'modified_field' => $modified_field,
  555. 'module_description' => $module_description,
  556. 'module_name' => $module_name,
  557. 'module_name_lower' => $module_name_lower,
  558. 'primary_key_field' => $primary_key_field,
  559. 'role_id' => $role_id,
  560. 'soft_delete_field' => $soft_delete_field,
  561. 'table_as_field_prefix' => $table_as_field_prefix,
  562. 'table_name' => $table_name,
  563. 'textarea_editor' => $textarea_editor,
  564. 'useCreated' => $useCreated,
  565. 'useModified' => $useModified,
  566. 'usePagination' => $usePagination,
  567. 'useSoftDeletes' => $useSoftDeletes,
  568. )
  569. );
  570. // Make the variables available to the view file
  571. $data['module_name'] = $module_name;
  572. $data['controller_name'] = $controller_name;
  573. $data['module_name_lower'] = $module_name_lower;
  574. $data['table_name'] = empty($table_name) ? $module_name_lower : $table_name;
  575. $data = $data + $file_data;
  576. // @todo Need to test whether this section can be removed... Only the
  577. // migrations library should know anything about the structure of the
  578. // migration versions in the database.
  579. //
  580. // Allow for the Old method - update the schema first to prevent errors
  581. // in duplicate column names due to Migrations.php caching db columns
  582. if (! $this->db->field_exists('version', 'schema_version')) {
  583. $this->dbforge->add_column(
  584. 'schema_version',
  585. array(
  586. $data['module_name_lower'] . '_version' => array(
  587. 'type' => 'INT',
  588. 'constraint' => 4,
  589. 'null' => true,
  590. 'default' => 0,
  591. ),
  592. )
  593. );
  594. }
  595. // Load the migrations library
  596. $this->load->library('migrations/migrations');
  597. // Run the migration install routine
  598. if ($this->migrations->install("{$data['module_name_lower']}_")) {
  599. $data['mb_migration_result'] = 'mb_out_tables_success';
  600. } else {
  601. $data['mb_migration_result'] = 'mb_out_tables_error';
  602. }
  603. Template::set($data);
  604. }
  605. /**
  606. * Verify that the Modules folder is writable
  607. *
  608. * @return bool True if the folder is writable, else false.
  609. */
  610. private function checkWritable()
  611. {
  612. return is_writable($this->options['output_path']);
  613. }
  614. //--------------------------------------------------------------------------
  615. // Form validation callbacks
  616. //--------------------------------------------------------------------------
  617. /**
  618. * Form validation callback for the module name
  619. *
  620. * @param string $str String to check
  621. *
  622. * @return bool
  623. */
  624. public function _modulename_check($str)
  625. {
  626. if (! preg_match("/^([A-Za-z \-]+)$/", $str)) {
  627. $this->form_validation->set_message('_modulename_check', lang('mb_modulename_check'));
  628. return false;
  629. }
  630. if (class_exists($str)) {
  631. $this->form_validation->set_message('_modulename_check', lang('mb_modulename_check_class_exists'));
  632. return false;
  633. }
  634. return true;
  635. }
  636. /**
  637. * Custom Form Validation Callback Rule
  638. *
  639. * Checks that one field doesn't match all the others.
  640. * This code is not really portable. Would have been nice to create a rule
  641. * that accepted an array
  642. *
  643. * @param string $str String to check against the other fields
  644. * @param array $fieldno The field number of this field
  645. *
  646. * @return bool
  647. */
  648. public function _no_match($str, $fieldno)
  649. {
  650. for ($counter = 1; $this->field_total >= $counter; $counter++) {
  651. // Nothing has been entered into the current field or the current
  652. // field is the same as the field to validate
  653. if ($_POST["view_field_name$counter"] == '' || $fieldno == $counter) {
  654. continue;
  655. }
  656. if ($str == $_POST["view_field_name{$counter}"]) {
  657. $this->form_validation->set_message(
  658. '_no_match',
  659. sprintf(
  660. lang('mb_validation_no_match'),
  661. lang('mb_form_field_details'),
  662. lang('mb_form_fieldname'),
  663. $fieldno,
  664. $counter
  665. )
  666. );
  667. return false;
  668. }
  669. }
  670. return true;
  671. }
  672. }