From d39e8b5d15291c1ccf846e5c2fad37959a31232e Mon Sep 17 00:00:00 2001 From: "M@d D3n" Date: Tue, 4 Aug 2020 13:46:39 +0300 Subject: [PATCH] Version 1.0 --- README.md | 18 +- admin/main.tpl | 104 +++++++++++ class/snippets.php | 455 +++++++++++++++++++++++++++++++++++++++++++++ data/fragment.php | 13 ++ index.php | 4 + info.php | 20 ++ js/snippets.js | 381 +++++++++++++++++++++++++++++++++++++ lang/ru.txt | 34 ++++ module.php | 69 +++++++ sql.php | 19 ++ 10 files changed, 1116 insertions(+), 1 deletion(-) create mode 100644 admin/main.tpl create mode 100644 class/snippets.php create mode 100644 data/fragment.php create mode 100644 index.php create mode 100644 info.php create mode 100644 js/snippets.js create mode 100644 lang/ru.txt create mode 100644 module.php create mode 100644 sql.php diff --git a/README.md b/README.md index b75014b..2a20285 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,18 @@ -# module-snippets +# Модуль - Сниппеты PHP +###### Позволяет хранить и выполнять произвольный php-код. +## Примеры кода +```php + +``` + +## Changelog: + +05.07.2020 - Дата создания + + +--- +Copyright © 2007-2020 [Ave-Cms.Ru](https://ave-cms.ru) \ No newline at end of file diff --git a/admin/main.tpl b/admin/main.tpl new file mode 100644 index 0000000..bc20c9a --- /dev/null +++ b/admin/main.tpl @@ -0,0 +1,104 @@ + + +
+
{#CONSOLE_MODULE_NAME#}
+
+ +
+
+ {#CONSOLE_EDIT_TIP#} +
+
+ + + +
+ +
+
+
+ {#CONSOLE_MODULE_NAME#} +
+
+ + + + + + + + + + + + + + + + +
+ {#CONSOLE_SETTINGS#} +
+ {#CONSOLE_SAVED_FRAGMENTS#} + + + + {#CONSOLE_REPORT_EXECUTION#} + + +
+ + + + + + + + + + + + +
+ {#CONSOLE_MODULE_NAME#} +
+ +
+ + + +
+
+ + + +{include file="$codemirror_connect"} +{include file="$codemirror_editor" textarea_id='code_text' ctrls='ModuleSnippets.saveFragment();' height='300'} \ No newline at end of file diff --git a/class/snippets.php b/class/snippets.php new file mode 100644 index 0000000..cafbf2e --- /dev/null +++ b/class/snippets.php @@ -0,0 +1,455 @@ +get_config_vars('CONSOLE_SAVE_NO_NAME')); + + $fragment_code = (isset($_POST['fragment_code']) + ? base64_encode($_POST['fragment_code']) + : ''); + + $fragmentdata = [ + 'fragment_id' => $key, + 'fragment_time' => time(), + 'fragment_ip' => $_SERVER['REMOTE_ADDR'], + 'fragment_user_id' => (isset($_SESSION['user_id']) ? $_SESSION['user_id'] : '0'), + 'fragment_name' => $fragment_name, + 'fragment_code' => $fragment_code + ]; + + return $fragmentdata; + } + + + /* + |----------------------------------------------------------------------------------------------------------------------- + | self::show + |----------------------------------------------------------------------------------------------------------------------- + | + */ + public static function show () + { + global $AVE_Template; + + $fragmentdata = []; + + if (file_exists(self::$fragmentdir . self::$fragmentfile)) + include self::$fragmentdir . self::$fragmentfile; + + $fragmentdata = msort($fragmentdata, 'fragment_name'); + + $AVE_Template->assign('fragments', $fragmentdata); + $AVE_Template->assign('content', $AVE_Template->fetch(self::$tpl_dir . 'main.tpl')); + } + + + /* + |----------------------------------------------------------------------------------------------------------------------- + | self::execute + |----------------------------------------------------------------------------------------------------------------------- + | + */ + public static function fragmentExecute () + { + global $AVE_DB, $AVE_Template; + + error_reporting(E_ALL); + ini_set('display_errors', 1); + + $profiler = false; + + if (isset($_POST['fragment_profiling']) && $_POST['fragment_profiling'] == 1) { + + static $list, $time, $count; + $profiler = true; + } + + $code = stripslashes($_POST['fragment_code']); + + ob_start(); + + Debug::startMemory('Eval'); + Debug::startTime('Eval'); + + $result = eval(' ?>' . $code . 'Query("SHOW PROFILES"); + + $list = "" + . "\n\t\n\t"; + + while ($row = $result_profiler->FetchRow()) { + $time += $row->Duration; + + $row->Query = preg_replace('/\t+/', '', $row->Query); + + $list .= "\n\t\n\t\t\n\t\t\n\t\t\n\t"; + } + + $list .= "\n
" + . $row->Query_ID + . "" + . number_format($row->Duration * 1, 6, ',', '') + . "" + . $row->Query + . "
"; + $time = number_format($time * 1, 6, ',', ''); + $count = $result_profiler->NumRows(); + + $AVE_DB->Query("SET PROFILING = 0"); + + $out .= ''; + $out .= "\n
" . $list; + } + else + { + $out .= ''; + } + + echo $out; + exit; + } + + + /* + |----------------------------------------------------------------------------------------------------------------------- + | self::fragmentInsert + |----------------------------------------------------------------------------------------------------------------------- + | + */ + public static function fragmentInsert () + { + global $AVE_Template; + + $fragment_code = ''; + + $fragmentdata = []; + + $fragment_key = (isset($_POST['fragment_key']) && is_numeric($_POST['fragment_key']) OR $_POST['fragment_key'] != '') + ? (int) $_POST['fragment_key'] + : null; + + if (file_exists(self::$fragmentdir . self::$fragmentfile)) + include self::$fragmentdir . self::$fragmentfile; + + if (isset($fragmentdata[$fragment_key])) + $fragment_code = stripslashes(base64_decode($fragmentdata[$fragment_key]['fragment_code'])); + + if (isAjax()) + { + $success = $fragment_code + ? true + : false; + + $return = [ + 'success' => $success, + 'fragment_code' => $fragment_code, + 'fragment_key' => $fragment_key, + 'message' => $success ? $AVE_Template->get_config_vars('CONSOLE_DOWNLOAD_SAVED') : $AVE_Template->get_config_vars('CONSOLE_DOWNLOAD_FALSE'), + 'header' => $success ? $AVE_Template->get_config_vars('CONSOLE_SUCCESS') : $AVE_Template->get_config_vars('CONSOLE_ERROR'), + 'theme' => $success ? 'accept' : 'error' + ]; + + self::_json($return, true); + } else { + header('Location:index.php?do=modules&action=modedit&mod=snippets&moduleaction=1&cp=' . SESSION); + exit; + } + } + + + /* + |----------------------------------------------------------------------------------------------------------------------- + | self::fragmentNew + |----------------------------------------------------------------------------------------------------------------------- + | + */ + public static function fragmentNew () + { + global $AVE_Template; + + $fragmentdata = []; + + $fragmentfile = self::$fragmentdir . self::$fragmentfile; + + if (file_exists(self::$fragmentdir . self::$fragmentfile)) + include self::$fragmentdir . self::$fragmentfile; + + if (empty($fragmentdata)) + $_key = 0; + else + $_key = array_pop(array_keys($fragmentdata)) + 1; + + $fragmentdata[] = self::_fragment($_key); + + $fragmentdata = array_slice($fragmentdata, -1 * self::$limit); + + end($fragmentdata); + + $fragment_key = key($fragmentdata); + + $save_data = ''; + + file_put_contents($fragmentfile, $save_data); + + $fragmentdata = msort($fragmentdata, 'fragment_name'); + + $fragments = []; + + foreach ($fragmentdata AS $k => $v) + { + if ($v['fragment_id'] == $_key) + $_fragment_key = $v['fragment_id']; + + $fragments[$k] = [ + 'fragment_key' => $v['fragment_id'], + 'fragment_name' => $v['fragment_name'] + ]; + } + + unset ($fragmentdata, $save_data); + + if (isAjax()) { + $return = [ + 'success' => true, + 'key' => $_fragment_key, + 'fragments' => $fragments, + 'message' => $AVE_Template->get_config_vars('CONSOLE_SAVED'), + 'header' => $AVE_Template->get_config_vars('CONSOLE_SUCCESS'), + 'theme' => 'accept' + ]; + + self::_json($return, true); + } else { + header('Location:index.php?do=modules&action=modedit&mod=snippets&moduleaction=1&cp=' . SESSION); + exit; + } + } + + + /* + |----------------------------------------------------------------------------------------------------------------------- + | self::fragmentSave + |----------------------------------------------------------------------------------------------------------------------- + | + */ + public static function fragmentSave () + { + global $AVE_Template; + + $fragment_key = (int)$_POST['fragment_key']; + + $fragmentdata = []; + + $fragmentfile = self::$fragmentdir . self::$fragmentfile; + + if (file_exists(self::$fragmentdir . self::$fragmentfile)) + include self::$fragmentdir . self::$fragmentfile; + + $fragmentdata[$fragment_key]['fragment_code'] = base64_encode($_POST['fragment_code']); + $fragmentdata[$fragment_key]['fragment_changed'] = time(); + + $fragmentdata = array_slice ($fragmentdata, -1 * self::$limit); + + $save_data = ''; + + file_put_contents ($fragmentfile, $save_data); + + unset ($fragmentdata, $save_data); + + if (isAjax()) + { + $return = [ + 'success' => true, + 'key' => $fragment_key, + 'message' => $AVE_Template->get_config_vars('CONSOLE_SAVED'), + 'header' => $AVE_Template->get_config_vars('CONSOLE_SUCCESS'), + 'theme' => 'accept' + ]; + + self::_json($return, true); + } + else + { + header('Location:index.php?do=modules&action=modedit&mod=snippets&moduleaction=1&cp=' . SESSION); + exit; + } + } + + + /* + |----------------------------------------------------------------------------------------------------------------------- + | self::fragmentDelete + |----------------------------------------------------------------------------------------------------------------------- + | + */ + public static function fragmentDelete () + { + global $AVE_Template; + + $fragment_key = (int) $_POST['fragment_key']; + + $fragmentdata = []; + + $fragmentfile = self::$fragmentdir . self::$fragmentfile; + + if (file_exists(self::$fragmentdir . self::$fragmentfile)) + include self::$fragmentdir . self::$fragmentfile; + + foreach ($fragmentdata AS $k => $v) + { + if ($fragment_key == $v['fragment_id']) + unset ($fragmentdata[$k]); + } + + $fragmentdata = array_slice($fragmentdata, -1 * self::$limit); + + foreach ($fragmentdata AS $k => $v) + { + $v['fragment_id'] = $k; + $fragmentdata[$k] = $v; + } + + $save_data = ''; + + file_put_contents ($fragmentfile, $save_data); + + $fragmentdata = msort($fragmentdata, 'fragment_name'); + + $fragments = []; + + foreach ($fragmentdata AS $k => $v) + $fragments[] = [ + 'fragment_key' => $v['fragment_id'], + 'fragment_name' => $v['fragment_name'] + ]; + + unset ($fragmentdata, $save_data); + + if (isAjax()) { + $return = [ + 'success' => true, + 'fragments' => $fragments, + 'message' => $AVE_Template->get_config_vars('CONSOLE_DEL_SAVED'), + 'header' => $AVE_Template->get_config_vars('CONSOLE_SUCCESS'), + 'theme' => 'accept' + ]; + + self::_json($return, true); + } else { + header('Location:index.php?do=modules&action=modedit&mod=snippets&moduleaction=1&cp=' . SESSION); + exit; + } + } + + + /* + |----------------------------------------------------------------------------------------------------------------------- + | self::fragmentsShow + |----------------------------------------------------------------------------------------------------------------------- + | + */ + public static function fragmentsShow () + { + //ToDo + } + } +?> \ No newline at end of file diff --git a/data/fragment.php b/data/fragment.php new file mode 100644 index 0000000..a4d3fbd --- /dev/null +++ b/data/fragment.php @@ -0,0 +1,13 @@ + + array ( + 'fragment_id' => 0, + 'fragment_time' => 1596529593, + 'fragment_ip' => '::1', + 'fragment_user_id' => 1, + 'fragment_name' => 'Данные документа', + 'fragment_code' => 'PD9waHAKCURlYnVnOjpfZWNobyhnZXRfZG9jdW1lbnQoMSkpOwo/Pg==', + ), +) +?> \ No newline at end of file diff --git a/index.php b/index.php new file mode 100644 index 0000000..11b1a2d --- /dev/null +++ b/index.php @@ -0,0 +1,4 @@ + \ No newline at end of file diff --git a/info.php b/info.php new file mode 100644 index 0000000..7cddca8 --- /dev/null +++ b/info.php @@ -0,0 +1,20 @@ + 'snippets', + 'ModuleVersion' => '1.0', + 'ModuleAutor' => 'AVE.cms', + 'ModuleCopyright' => '© 2007-' . date('Y') . ' AVE.cms', + 'ModuleStatus' => 1, + 'ModuleIsFunction' => 0, + 'ModuleTemplate' => 0, + 'ModuleAdminEdit' => 1, + 'ModuleFunction' => 'mod_snippets', + 'ModuleTag' => null, + 'ModuleTagLink' => null, + 'ModuleAveTag' => null, + 'ModulePHPTag' => null + ]; +?> \ No newline at end of file diff --git a/js/snippets.js b/js/snippets.js new file mode 100644 index 0000000..a969cce --- /dev/null +++ b/js/snippets.js @@ -0,0 +1,381 @@ +var ModuleSnippets = { + + initialized: false, + + init: function () { + + if (this.initialized) + return; + + this.initialized = true; + + this.build(); // Main functions + this.events(); // Events functions + }, + + + // Main functions + build: function () { + this.executeBtn(); + this.saveBtn(); + this.clearBtn(); + this.deleteBtn(); + this.nameSelection(); + }, + + + // Events functions + events: function () { + this.MouseTrap(); + }, + + + // + MouseTrap: function () { + + Mousetrap.bind(['ctrl+enter', 'command+enter'], function (event) { + if (event.preventDefault) { + event.preventDefault(); + } else { + event.returnValue = false; + } + + ModuleSnippets.executeRun(); + + return false; + }); + + Mousetrap.bind(['ctrl+s', 'command+s'], function (event) { + + if (event.preventDefault) { + event.preventDefault(); + } else { + event.returnValue = false; + } + + ModuleSnippets.saveFragment(); + + return false; + }); + }, + + + // + saveFragment: function () { + + let key_fragment = $('#fragmentKey').val(); + + if (key_fragment.length == '') { + jPrompt(new_text, '', new_title, function (name) { + if (name) { + ModuleSnippets.saveNew(name); + } + }); + } + else { + ModuleSnippets.saveExist(key_fragment); + } + }, + + + // + saveNew: function (name) { + + let code = $("#code_text").val(); + + $.ajax({ + type: "POST", + url: "index.php?do=modules&action=modedit&mod=snippets&moduleaction=new&cp=" + session, + data: { + "fragment_name": name, + "fragment_code": code + }, + dataType: "json", + beforeSend: function () { + $.alerts._overlay('show'); + }, + success: function (data) { + $.alerts._overlay('hide'); + + $.jGrowl(data['message'], { + header: data['header'], + theme: data['theme'] + }); + + ModuleSnippets.changeSelect(data['fragments'], data['key']); + $('#fragmentKey').val(data['key']); + ModuleSnippets.showHideDeleteBtn(); + } + }); + }, + + + // + saveExist: function (key) { + let code = $("#code_text").val(); + + $.ajax({ + type: "POST", + url: "index.php?do=modules&action=modedit&mod=snippets&moduleaction=save&cp=" + session, + data: { + "fragment_key": key, + "fragment_code": code + }, + dataType: "json", + beforeSend: function () { + $.alerts._overlay('show'); + }, + success: function (data) { + $.alerts._overlay('hide'); + + $.jGrowl(data['message'], { + header: data['header'], + theme: data['theme'] + }); + } + }); + }, + + + // + executeRun: function () { + let code = $("#code_text").val(); + let profiling = $("#profilingCheck").is(':checked') ? 1 : 0; + + var Dialog = $('').appendTo('body'); + var dialogWidth = $(window).width() * 0.9; + var dialogHeight = $(window).height() * 0.8; + + $.ajax({ + type: "POST", + url: "index.php?do=modules&action=modedit&mod=snippets&moduleaction=execute&cp=" + session, + data: { + 'fragment_code': code, + 'fragment_profiling': profiling + }, + beforeSend: function () { + $.alerts._overlay('show'); + }, + success: function (data) { + $.alerts._overlay('hide'); + + Dialog.html(data); + } + }); + + Dialog.dialog({ + autoOpen: false, + modal: true, + dialogClass: 'fixed-dialog', + close: function (event, ui) { + $(this).dialog('destroy').remove(); + } + }); + + Dialog.dialog("option", "title", result_dialog); + Dialog.dialog("option", "width", dialogWidth); + Dialog.dialog("option", "height", dialogHeight); + Dialog.dialog("open"); + }, + + + // + executeBtn: function () { + $('.Execute').on('click', function (event) { + event.preventDefault(); + + ModuleSnippets.executeRun(); + + }); + }, + + + // + saveBtn: function () { + $('.SaveFragment').on('click', function (event) { + event.preventDefault(); + + ModuleSnippets.saveFragment(); + }); + }, + + + // + clearBtn: function () { + $('.Clear').on('click', function (event) { + event.preventDefault(); + $.alerts._overlay('show'); + $('#fragmentKey').val(''); + $('#fargmentNames option').prop('selected', false); + $('#fargmentNames option:first').prop('selected', true); + $("#code_text").text(ModuleSnippets.insertCodeMirror('')); + $('#fargmentNames').trigger('refresh'); + $('a.Delete').addClass('hidden'); + $.alerts._overlay('hide'); + }); + }, + + + // + deleteBtn: function () { + $('.Delete').on('click', function (event) { + jConfirm( + del_confirm, + del_title, + function (succes) { + if (succes) { + $.alerts._overlay('show'); + ModuleSnippets.deleteFragment(); + } + } + ); + }); + }, + + + // + deleteFragment: function () { + + let key_fragment = $('#fragmentKey').val(); + + if (key_fragment != '') { + $.ajax({ + type: "POST", + url: "index.php?do=modules&action=modedit&mod=snippets&moduleaction=delete&cp=" + session, + data: { + 'fragment_key': key_fragment + }, + dataType: "json", + beforeSend: function () { + $.alerts._overlay('show'); + }, + success: function(data) { + $.alerts._overlay('hide'); + + $.jGrowl(data['message'], { + header: data['header'], + theme: data['theme'] + }); + + $('#fragmentKey').val(''); + + $("#code_text").text(ModuleSnippets.insertCodeMirror('')); + ModuleSnippets.showHideDeleteBtn(); + ModuleSnippets.changeSelect(data['fragments']); + } + }); + } + }, + + + // + nameSelection: function () { + $('#fargmentNames').on('change', function (event) { + event.preventDefault(); + + let selected = $($(this), 'option:selected').val(); + + if (selected === '') + { + $('#fragmentKey').val(''); + + ModuleSnippets.showHideDeleteBtn(); + + return false; + } + + $.ajax({ + type: "POST", + url: "index.php?do=modules&action=modedit&mod=snippets&moduleaction=insert&cp=" + session, + data: { + 'fragment_key': selected + }, + dataType: "json", + beforeSend: function () { + $.alerts._overlay('show'); + }, + success: function (data) { + $.alerts._overlay('hide'); + + if (data.fragment_key == null) + { + $('#fragmentKey').val(''); + + ModuleSnippets.showHideDeleteBtn(); + + return false; + } + + $.jGrowl(data['message'], { + header: data['header'], + theme: data['theme'] + }); + + if (data['success']) + { + $("#code_text").text(ModuleSnippets.insertCodeMirror(data['fragment_code'])); + $('#fragmentKey').val(data['fragment_key']); + } + // ToDo ELSE + + ModuleSnippets.showHideDeleteBtn(); + } + }); + + return false; + }); + }, + + + // + insertCodeMirror: function (data) { + var cm = $('.CodeMirror')[0].CodeMirror; + var doc = cm.getDoc(); + var cursor = doc.getCursor(); + var line = doc.getLine(cursor.line); + var pos = { + line: cursor.line, + ch: line.length - 1 + }; + + doc.setValue(''); + + doc.replaceRange(data, pos); + }, + + + // + showHideDeleteBtn: function () { + let key_fragment = $('#fragmentKey').val(); + + (key_fragment == '') + ? $('a.Delete').addClass('hidden') + : $('a.Delete').removeClass('hidden'); + + return false; + }, + + + // + changeSelect: function (data, key) { + let select = $('#fargmentNames'); + + select.html(''); + + select.append('') + + $.each(data, function () { + select.append('') + }); + + if (key !== undefined) + select.children('option[value=' + key + ']').prop('selected', true); + + $('select').trigger('refresh'); + } +}; + +jQuery(function ($) { + "use strict"; + ModuleSnippets.init(); +}); \ No newline at end of file diff --git a/lang/ru.txt b/lang/ru.txt new file mode 100644 index 0000000..79138a4 --- /dev/null +++ b/lang/ru.txt @@ -0,0 +1,34 @@ +[name] +MODULE_NAME = "Сниппеты PHP" +MODULE_DESCRIPTION = "Позволяет хранить и выполнять произвольный php-код." + +[admin] +CONSOLE_MODULE_NAME = "Сниппеты PHP" +CONSOLE_EDIT_TIP = "Позволяет хранить и выполнять произвольный php-код." +CONSOLE_BUTTON_ENTER = "Выполнить" +CONSOLE_BUTTON_SAVE = "Сохранить" +CONSOLE_BUTTON_DOWNLOAD = "Загрузить" +CONSOLE_BUTTON_CLEAR = "Очистить" +CONSOLE_BUTTON_CLOSE = "Закрыть" +CONSOLE_PERFORMED = "Результат" +CONSOLE_SETTINGS = "Настройки" +CONSOLE_REPORT_EXECUTION = "Отчет выполнения" +CONSOLE_SUCCESS = "Выполнено" +CONSOLE_ERROR = "Ошибка" +CONSOLE_CLEAR = "Консоль очищена" +CONSOLE_SELECT_FRAGMENT = "-- Выберите сниппет --" +CONSOLE_DOWNLOAD_FRAGMENT = "Загрузить" +CONSOLE_DELETE_FRAGMENT = "Удалить" +CONSOLE_CLOSE_FRAGMENT = "Закрыть" +CONSOLE_SAVED = "Сниппет успешно добавлен" +CONSOLE_DEL_SAVED = "Сниппет успешно удален" +CONSOLE_DOWNLOAD_SAVED = "Сниппет успешно загружен в консоль" +CONSOLE_DOWNLOAD_FALSE = "Сниппет не был загружен в консоль" +CONSOLE_EDIT = "Редактировать сниппет" +CONSOLE_DELETE = "Удалить сниппет" +CONSOLE_DELETE_CONFIRM = "Вы уверенны, что хотите удалить сниппет" +CONSOLE_NEW_TITLE = "Создание нового сниппета" +CONSOLE_NEW_TEXT = "Пожалуйста, укажите наименование сниппета" +CONSOLE_DATA_TITLE = "Сниппеты кода" +CONSOLE_HELP = "Сочетание клавиш
   Сохранить (CTRL+S)
   Загрузить (CTRL+D)
   Очистить (CTRL+С)" +CONSOLE_SAVED_FRAGMENTS = "Сохраненные сниппеты" \ No newline at end of file diff --git a/module.php b/module.php new file mode 100644 index 0000000..9ddc8f6 --- /dev/null +++ b/module.php @@ -0,0 +1,69 @@ +config_load($lang_file, 'admin'); + + //-- Actions + switch ($_REQUEST['moduleaction']) + { + case '1': + Snippets::show(); + break; + + case 'execute': + Snippets::fragmentExecute(); + break; + + case 'insert': + Snippets::fragmentInsert(); + break; + + case 'new': + Snippets::fragmentNew(); + break; + + case 'save': + Snippets::fragmentSave(); + break; + + case 'delete': + Snippets::fragmentDelete(); + break; + + case 'fragments': + Snippets::fragmentsShow(); + break; + } + } + +?> diff --git a/sql.php b/sql.php new file mode 100644 index 0000000..82bb6fa --- /dev/null +++ b/sql.php @@ -0,0 +1,19 @@ + \ No newline at end of file