From 7bbd206861297ee0372a01eee7f02cddace47e7c Mon Sep 17 00:00:00 2001 From: Martin Beckmann Date: Wed, 20 May 2015 00:05:02 +0200 Subject: [PATCH 1/6] We should now validate against branch 3.1.x --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index da58eb7..68339bc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,7 +33,7 @@ env: - SNIFF="1" # Should we run code sniffer on your code? - IMAGE_ICC="1" # Should we run icc profile sniffer on your images? - EPV="1" # Should we run EPV (Extension Pre Validator) on your code? - - PHPBB_BRANCH="develop-ascraeus" + - PHPBB_BRANCH="3.1.x" branches: only: From ab2e802482b3c783504d0116ef8cc7ebb470891f Mon Sep 17 00:00:00 2001 From: Martin Beckmann Date: Sat, 13 Jun 2015 03:53:48 +0200 Subject: [PATCH 2/6] Edit permissions for pastebin, cleanup --- composer.json | 2 +- config/services.yml | 9 +- controller/main.php | 112 +++++++----- event/acp_events.php | 2 + functions/pastebin.php | 179 ++++++++++++------- functions/utility.php | 101 +++++++++++ language/de/permissions_pastebin.php | 2 + language/en/permissions_pastebin.php | 2 + migrations/v_0_0_1.php | 31 ++++ styles/prosilver/template/pastebin_body.html | 4 +- 10 files changed, 336 insertions(+), 108 deletions(-) create mode 100644 functions/utility.php create mode 100644 migrations/v_0_0_1.php diff --git a/composer.json b/composer.json index acb0327..bbb9ce8 100644 --- a/composer.json +++ b/composer.json @@ -3,7 +3,7 @@ "type": "phpbb-extension", "description": "Provides a pastebin including syntax highlighting", "homepage": "https://www.phpbb.de/community/", - "version": "1.0.0", + "version": "1.0.0-dev", "time": "2015-01-03", "license": "GPL-2.0", "authors": [ diff --git a/config/services.yml b/config/services.yml index f8089cb..99d50f7 100644 --- a/config/services.yml +++ b/config/services.yml @@ -5,10 +5,16 @@ parameters: phpbbde.pastebin.cron.prune_interval: 86400 tables.phpbbde.pastebin.pastebin: %core.table_prefix%pastebin services: + phpbbde.pastebin.functions.utility: + class: phpbbde\pastebin\functions\utility + arguments: + - %phpbbde.pastebin.geshilangs% phpbbde.pastebin.functions.pastebin: class: phpbbde\pastebin\functions\pastebin arguments: - - %phpbbde.pastebin.geshilangs% + - @dbal.conn + - @user + - %tables.phpbbde.pastebin.pastebin% phpbbde.pastebin.controller.main: class: phpbbde\pastebin\controller\main arguments: @@ -21,6 +27,7 @@ services: - @user - @controller.helper - @captcha.factory + - @phpbbde.pastebin.functions.utility - @phpbbde.pastebin.functions.pastebin - %core.root_path% - %core.php_ext% diff --git a/controller/main.php b/controller/main.php index 1edb04b..4ef9053 100644 --- a/controller/main.php +++ b/controller/main.php @@ -54,6 +54,9 @@ class main /** @var \phpbbde\pastebin\functions\pastebin */ protected $pastebin; + /** @var \phpbbde\pastebin\functions\utility */ + protected $util; + /** @var \phpbb\captcha\factory */ protected $captcha_factory; @@ -77,10 +80,11 @@ class main * @param \phpbb\user $user * @param \phpbb\controller\helper $helper * @param \phpbbde\pastebin\functions\pastebin $pastebin + * @param \phpbbde\pastebin\functions\utility $util * @param string $root_path * @param string $php_ext */ - public function __construct(\phpbb\auth\auth $auth, \phpbb\cache\service $cache, \phpbb\config\config $config, \phpbb\request\request $request, \phpbb\db\driver\driver_interface $db, \phpbb\template\template $template, \phpbb\user $user, \phpbb\controller\helper $helper, \phpbb\captcha\factory $captcha_factory, \phpbbde\pastebin\functions\pastebin $pastebin, $root_path, $php_ext, $geshi_path, $geshi_lang, $pastebin_table) + public function __construct(\phpbb\auth\auth $auth, \phpbb\cache\service $cache, \phpbb\config\config $config, \phpbb\request\request $request, \phpbb\db\driver\driver_interface $db, \phpbb\template\template $template, \phpbb\user $user, \phpbb\controller\helper $helper, \phpbb\captcha\factory $captcha_factory, \phpbbde\pastebin\functions\utility $util, \phpbbde\pastebin\functions\pastebin $pastebin, $root_path, $php_ext, $geshi_path, $geshi_lang, $pastebin_table) { $this->auth = $auth; $this->cache = $cache; @@ -93,6 +97,7 @@ public function __construct(\phpbb\auth\auth $auth, \phpbb\cache\service $cache, $this->root_path = $root_path; $this->php_ext = $php_ext; $this->pastebin = $pastebin; + $this->util = $util; $this->captcha_factory = $captcha_factory; $this->geshi_path = $geshi_path; @@ -140,7 +145,7 @@ private function table($name) */ private function display_pb() { - $pastebin = $this->pastebin; + $util = $this->util; $template = $this->template; $db = $this->db; $auth = $this->auth; @@ -150,8 +155,43 @@ private function display_pb() $mode = $this->request->variable('mode', ''); $confirm_id = $this->request->variable('confirm_id', ''); $confirm_code = $this->request->variable('confirm_code', ''); + $snippet_id = $this->request->variable('s', 0); $submit = isset($_POST['submit']) ? true : false; + if(in_array($mode, array('view', 'download', 'moderate'))) + { + // for all of these we have to check if the entry exists + + $sql = $db->sql_build_query('SELECT', array( + 'SELECT' => 'pb.*, u.user_id, u.username, u.user_colour', + 'FROM' => array( + $this->table('pastebin') => 'pb', + USERS_TABLE => 'u', + ), + 'WHERE' => "pb.snippet_author = u.user_id AND pb.snippet_id = $snippet_id", + )); + $result = $db->sql_query($sql); + $data = $db->sql_fetchrow($result); + $db->sql_freeresult($result); + + if (!$data) + { + $message = $user->lang['NO_VALID_SNIPPET']; + $message .= '

'; + $message .= sprintf($user->lang['RETURN_PASTEBIN'], '', ''); + + trigger_error($message); + } + + $this->pastebin->load_from_array($data); + $snippet = $this->pastebin; + + $this->template->assign_vars(array( + 'S_AUTH_EDIT' => ($auth->acl_get('m_pastebin_edit') || ($auth->acl_get('u_pastebin_edit') && $snippet['snippet_author'] == $this->user->data['user_id'])) ? true : false, + 'S_AUTH_DELETE' => ($auth->acl_get('m_pastebin_delete') || ($auth->acl_get('u_pastebin_delete') && $snippet['snippet_author'] == $this->user->data['user_id'])) ? true : false, + )); + } + // Some default values $error = $s_hidden_fields = array(); @@ -188,10 +228,9 @@ private function display_pb() 'S_AUTH_VIEW' => ($auth->acl_get('u_pastebin_view')) ? true : false, 'S_AUTH_POST' => ($auth->acl_get('u_pastebin_post')) ? true : false, - 'S_AUTH_EDIT' => ($auth->acl_get('m_pastebin_edit')) ? true : false, - 'S_AUTH_DELETE' => ($auth->acl_get('m_pastebin_delete')) ? true : false, )); + // Now let's decide what to do switch ($mode) { @@ -229,7 +268,7 @@ private function display_pb() $error[] = $user->lang['ERR_NO_TITLE']; } - if (!$pastebin->geshi_check($data['snippet_highlight'])) + if (!$util->geshi_check($data['snippet_highlight'])) { $data['snippet_highlight'] = 'text'; } @@ -325,30 +364,9 @@ private function display_pb() case 'download': case 'moderate': - // for all of these we have to check if the entry exists - $snippet_id = $this->request->variable('s', 0); - $sql = $db->sql_build_query('SELECT', array( - 'SELECT' => 'pb.*, u.user_id, u.username, u.user_colour', - 'FROM' => array( - $this->table('pastebin') => 'pb', - USERS_TABLE => 'u', - ), - 'WHERE' => "pb.snippet_author = u.user_id AND pb.snippet_id = $snippet_id", - )); - $result = $db->sql_query($sql); - $data = $db->sql_fetchrow($result); - $db->sql_freeresult($result); - if (!$data) - { - $message = $user->lang['NO_VALID_SNIPPET']; - $message .= '

'; - $message .= sprintf($user->lang['RETURN_PASTEBIN'], '', ''); - - trigger_error($message); - } if ($mode == 'view') { @@ -363,7 +381,7 @@ private function display_pb() $highlight = (isset($_REQUEST['highlight'])) ? $this->request->variable('highlight', '') : $data['snippet_highlight']; - if (!$pastebin->geshi_check($highlight)) + if (!$util->geshi_check($highlight)) { $highlight = 'php'; } @@ -374,7 +392,7 @@ private function display_pb() $code = htmlspecialchars_decode($snippet_text); - $geshi = new \GeSHi($code, $highlight, $pastebin->geshi_dir); + $geshi = new \GeSHi($code, $highlight, $util->geshi_dir); $geshi->set_header_type(GESHI_HEADER_NONE); $geshi->enable_line_numbers(GESHI_NORMAL_LINE_NUMBERS, 100); @@ -400,7 +418,7 @@ private function display_pb() 'SNIPPET_AUTHOR_FULL' => get_username_string('full', $data['user_id'], $data['username'], $data['user_colour']), 'SNIPPET_DATE' => $user->format_date($data['snippet_time']), - 'HIGHLIGHT_SELECT_MOD' => $pastebin->highlight_select($data['snippet_highlight']), + 'HIGHLIGHT_SELECT_MOD' => $util->highlight_select($data['snippet_highlight']), 'DOWNLOAD_SNIPPET_EXPLAIN' => sprintf($user->lang['DOWNLOAD_SNIPPET_EXPLAIN'], '', ''), 'U_SNIPPET' => $this->helper->route('phpbbde_pastebin_main_controller', array("mode" => "view", "s" => $data['snippet_id'])), @@ -453,11 +471,15 @@ private function display_pb() else if ($mode == 'moderate') { $delete = (isset($_POST['delete_snippet'])) ? true : false; - $prunable = (isset($_POST['snippet_prunable'])) ? true : false; $highlight = $this->request->variable('snippet_highlight', ''); - $pruning_months = max(1, min(6, $this->request->variable('pruning_months', 0))); + $pruning_months = $this->request->variable('pruning_months', 0); + $prunable = $pruning_months != -1; - if (!$auth->acl_get('m_pastebin_edit') || ($delete && !$auth->acl_get('m_pastebin_delete'))) + $auth_edit = ($auth->acl_get('m_pastebin_edit') || ($auth->acl_get('u_pastebin_edit') && $this->user->data['user_id'] == $snippet['snippet_author'])); + $auth_delete = ($auth->acl_get('m_pastebin_delete') || ($auth->acl_get('u_pastebin_delete') && $this->user->data['user_id'] == $snippet['snippet_author'])); + + // Generic permissions check + if (!$auth_edit && !$auth_delete) { trigger_error('PASTEBIN_AUTH_NO_VIEW'); } @@ -468,7 +490,7 @@ private function display_pb() redirect($this->helper->route('phpbbde_pastebin_main_controller', array("mode"=>"view","s"=>$snippet_id))); } - if ($delete) + if ($delete && $auth_delete) { // Confirm box if (!confirm_box(true)) @@ -478,21 +500,25 @@ private function display_pb() } else { - $sql = 'DELETE FROM ' . $this->table('pastebin') . ' - WHERE snippet_id = ' . $snippet_id; + $snippet->delete(); $redirect_append = array(); } } - else + else if($auth_edit) { - $sql = 'UPDATE ' . $this->table('pastebin') . ' SET ' . $db->sql_build_array('UPDATE', array( - 'snippet_prunable' => (int) $prunable, - 'snippet_highlight' => $highlight, - 'snippet_prune_on' => $row['snippet_time'] + ($pruning_months * $this::SECONDS_MONTH), - )) . ' WHERE snippet_id = ' . $snippet_id; + $snippet->load_from_array(array( + 'snippet_prunable' => (int) $prunable, + 'snippet_highlight' => $highlight, + 'snippet_prune_on' => $data['snippet_time'] + ($pruning_months * $this::SECONDS_MONTH), + )); + $snippet->submit(); + $redirect_append = array("mode"=>"view","s"=>$snippet_id); } - $db->sql_query($sql); + else + { + trigger_error('PASTEBIN_NOT_AUTH_EDIT'); + } $redirect_url = $this->helper->route('phpbbde_pastebin_main_controller', $redirect_append); @@ -566,7 +592,7 @@ private function display_pb() { $highlight = isset($data['snippet_highlight']) ? $data['snippet_highlight'] : 'php'; } - $highlight_select = $pastebin->highlight_select($highlight); + $highlight_select = $util->highlight_select($highlight); add_form_key('pastebinform'); diff --git a/event/acp_events.php b/event/acp_events.php index 6fa8b96..ee24806 100644 --- a/event/acp_events.php +++ b/event/acp_events.php @@ -50,6 +50,8 @@ public function add_permissions($event) 'u_pastebin_post' => array('lang' => 'ACL_U_PASTEBIN_POST', 'cat' => 'pastebin'), 'u_pastebin_post_novc' => array('lang' => 'ACL_U_PASTEBIN_POST_NOVC', 'cat' => 'pastebin'), 'u_pastebin_post_notlim' => array('lang' => 'ACL_U_PASTEBIN_POST_NOTLIM', 'cat' => 'pastebin'), + 'u_pastebin_edit' => array('lang' => 'ACL_U_PASTEBIN_EDIT', 'cat' => 'pastebin'), + 'u_pastebin_delete' => array('lang' => 'ACL_U_PASTEBIN_DELETE', 'cat' => 'pastebin'), // Moderator perms 'm_pastebin_edit' => array('lang' => 'ACL_M_PASTEBIN_EDIT', 'cat' => 'pastebin'), diff --git a/functions/pastebin.php b/functions/pastebin.php index 916e72b..249d39b 100644 --- a/functions/pastebin.php +++ b/functions/pastebin.php @@ -1,101 +1,156 @@ geshi_dir = $geshi_dir; - $this->geshi_list = $this->geshi_list(); - } + $this->db = $db; + $this->user = $user; + $this->pastebin_table = $pastebin_table; + $this->empty_data(); + } /** - * Check if $needle is in one of geshis supported languages + * Removes all pastebin data and replaces them by the default. */ - function geshi_check($needle) + function empty_data() { - return in_array($needle, $this->geshi_list); + $this->data = array( + 'snippet_id' => 0, + 'snippet_author' => $this->user->data['user_id'], + 'snippet_time' => time(), + 'snippet_prune_on' => 0, + 'snippet_title' => '', + 'snippet_desc' => '', + 'snippet_text' => '', + 'snippet_prunable' => false, + 'snippet_highlight' => 'text', + ); } /** - * List of all geshi langs + * Load pastebin from DB. Returns true if entry was found, false otherwise + * @param int $id + * @return boolean */ - function geshi_list() + function load($id) { - global $phpEx; + $sql = 'SELECT * FROM ' . $this->pastebin_table . ' WHERE snippet_id = ' . (int) $id; + $result = $this->db->sql_query($sql); + $row = $this->db->sql_fetchrow($result); + if($row) + { + $this->data = $row; + return true; + } - $geshi_list = array(); + return false; + } - $d = dir($this->geshi_dir); - while (false !== ($file = $d->read())) + /** + * Load changes from array. Unknown entries are ignored. + * + * @param array $data + */ + function load_from_array($data) + { + foreach($this->data as $key => $value) { - if (in_array($file, array('.', '..'))) - { - continue; - } - - if (($substr_end = strpos($file, ".$phpEx")) !== false) + if(isset($data[$key])) { - @sort($geshi_list[] = substr($file, 0, $substr_end)); + $this->data[$key] = $data[$key]; } } - $d->close(); + } - return $geshi_list; + /** + * Store changes in the database + */ + function submit() + { + if($this->data['snippet_id']) + { + // Update + $sql = 'UPDATE ' . $this->pastebin_table . ' SET ' . $this->db->sql_build_array('UPDATE', $this->data) . ' WHERE snippet_id = ' . (int) $this->data['snippet_id']; + $this->db->sql_query($sql); + } + else + { + // Insert + $row = $this->data; + unset($row['snippet_id']); + $sql = 'INSERT INTO ' . $this->pastebin_table . ' ' . $this->db->sql_build_array('INSERT', $row); + $this->db->sql_query($sql); + $this->data['snippet_id'] = $this->db->sql_nextid(); + } } /** - * Highlight select box for geshi languages + * Deletes the current snippet from the database */ - function highlight_select($default = 'text') + function delete() + { + $sql = 'DELETE FROM ' . $this->pastebin_table . ' + WHERE snippet_id = ' . $this->data['snippet_id']; + $this->db->sql_query($sql); + $this->empty_data(); + } + + // ArrayAccess + // + + function offsetExists($offset) + { + return isset($this->data[$offset]); + } + + function offsetGet($offset) { - global $user; - if (!in_array($default, $this->geshi_list)) + if(!isset($this->data[$offset])) { - $default = 'text'; + throw new \Exception('Invalid offset'); } + return $this->data[$offset]; + } - $output = ''; - foreach ($user->lang['PASTEBIN_LANGUAGES'] as $code => $name) + function offsetSet($offset, $value) + { + if(!isset($this->data[$offset])) { - if (in_array($code, $this->geshi_list)) - { - $output .= '' . $name . ''; - } + throw new \Exception('Invalid offset'); } - return $output; + $this->data[$offset] = $value; + } + + function offsetUnset($offset) + { + } -} +} \ No newline at end of file diff --git a/functions/utility.php b/functions/utility.php new file mode 100644 index 0000000..cf3ed4e --- /dev/null +++ b/functions/utility.php @@ -0,0 +1,101 @@ +geshi_dir = $geshi_dir; + $this->geshi_list = $this->geshi_list(); + } + + + /** + * Check if $needle is in one of geshis supported languages + */ + function geshi_check($needle) + { + return in_array($needle, $this->geshi_list); + } + + /** + * List of all geshi langs + */ + function geshi_list() + { + global $phpEx; + + $geshi_list = array(); + + $d = dir($this->geshi_dir); + while (false !== ($file = $d->read())) + { + if (in_array($file, array('.', '..'))) + { + continue; + } + + if (($substr_end = strpos($file, ".$phpEx")) !== false) + { + @sort($geshi_list[] = substr($file, 0, $substr_end)); + } + } + $d->close(); + + return $geshi_list; + } + + /** + * Highlight select box for geshi languages + */ + function highlight_select($default = 'text') + { + global $user; + if (!in_array($default, $this->geshi_list)) + { + $default = 'text'; + } + + $output = ''; + foreach ($user->lang['PASTEBIN_LANGUAGES'] as $code => $name) + { + if (in_array($code, $this->geshi_list)) + { + $output .= '' . $name . ''; + } + } + + return $output; + } +} diff --git a/language/de/permissions_pastebin.php b/language/de/permissions_pastebin.php index b5662fe..8f54fcc 100644 --- a/language/de/permissions_pastebin.php +++ b/language/de/permissions_pastebin.php @@ -42,6 +42,8 @@ 'ACL_U_PASTEBIN_POST' => 'Kann Snippets posten', 'ACL_U_PASTEBIN_POST_NOVC' => 'Kann Snippets posten ohne visuelle Bestätigung', 'ACL_U_PASTEBIN_POST_NOTLIM' => 'Kann Snippets dauerhaft in den Pastebin einstellen', + 'ACL_U_PASTEBIN_EDIT' => 'Kann eigene Snippets bearbeiten', + 'ACL_U_PASTEBIN_DELETE' => 'Kann eigene Snippets löschen', // Moderator perms, 'ACL_M_PASTEBIN_EDIT' => 'Kann Snippets editieren', diff --git a/language/en/permissions_pastebin.php b/language/en/permissions_pastebin.php index 88b75c4..4938d3e 100644 --- a/language/en/permissions_pastebin.php +++ b/language/en/permissions_pastebin.php @@ -42,6 +42,8 @@ 'ACL_U_PASTEBIN_POST' => 'Can post pastebin entries', 'ACL_U_PASTEBIN_POST_NOVC' => 'Can post pastebin entries without visual confirmation', 'ACL_U_PASTEBIN_POST_NOTLIM' => 'Can post non-pruned pastebin entries', + 'ACL_U_PASTEBIN_EDIT' => 'Can edit own pastebin entries', + 'ACL_U_PASTEBIN_DELETE' => 'Can delete own pastebin entries', // Moderator perms 'ACL_M_PASTEBIN_EDIT' => 'Can edit pastebin entries', diff --git a/migrations/v_0_0_1.php b/migrations/v_0_0_1.php new file mode 100644 index 0000000..9a5fdeb --- /dev/null +++ b/migrations/v_0_0_1.php @@ -0,0 +1,31 @@ +{L_PASTEBIN} - +