Verzeichnisstruktur phpBB-3.3.16


Veröffentlicht
27.04.2026

So funktioniert es


Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück

Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis.
Auf den Verzeichnisnamen klicken, dies zeigt nur das Verzeichnis mit Inhalt an

(Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

qa.php

Zuletzt modifiziert: 01.05.2026, 11:25 - Dateigröße: 24.30 KiB


0001  <?php
0002  /**
0003  *
0004  * This file is part of the phpBB Forum Software package.
0005  *
0006  * @copyright (c) phpBB Limited <https://www.phpbb.com>
0007  * @license GNU General Public License, version 2 (GPL-2.0)
0008  *
0009  * For full copyright and license information, please see
0010  * the docs/CREDITS.txt file.
0011  *
0012  */
0013   
0014  namespace phpbb\captcha\plugins;
0015   
0016  /**
0017  * And now to something completely different. Let's make a captcha without extending the abstract class.
0018  * QA CAPTCHA sample implementation
0019  */
0020  class qa
0021  {
0022      var $confirm_id;
0023      var $answer;
0024      var $question_ids = [];
0025      var $question_text;
0026      var $question_lang;
0027      var $question_strict;
0028      var $attempts = 0;
0029      var $type;
0030      // dirty trick: 0 is false, but can still encode that the captcha is not yet validated
0031      var $solved = 0;
0032   
0033      protected $table_captcha_questions;
0034      protected $table_captcha_answers;
0035      protected $table_qa_confirm;
0036   
0037      /**
0038      * @var string name of the service.
0039      */
0040      protected $service_name;
0041   
0042      /**
0043      * Constructor
0044      *
0045      * @param string $table_captcha_questions
0046      * @param string $table_captcha_answers
0047      * @param string $table_qa_confirm
0048      */
0049      function __construct($table_captcha_questions, $table_captcha_answers, $table_qa_confirm)
0050      {
0051          $this->table_captcha_questions = $table_captcha_questions;
0052          $this->table_captcha_answers = $table_captcha_answers;
0053          $this->table_qa_confirm = $table_qa_confirm;
0054      }
0055   
0056      /**
0057      * @param int $type  as per the CAPTCHA API docs, the type
0058      */
0059      function init($type)
0060      {
0061          global $config, $db, $user, $request;
0062   
0063          // load our language file
0064          $user->add_lang('captcha_qa');
0065   
0066          // read input
0067          $this->confirm_id = $request->variable('qa_confirm_id', '');
0068          $this->answer = $request->variable('qa_answer', '', true);
0069   
0070          $this->type = (int) $type;
0071          $this->question_lang = $user->lang_name;
0072   
0073          // we need all defined questions - shouldn't be too many, so we can just grab them
0074          // try the user's lang first
0075          $sql = 'SELECT question_id
0076              FROM ' . $this->table_captcha_questions . "
0077              WHERE lang_iso = '" . $db->sql_escape($user->lang_name) . "'";
0078          $result = $db->sql_query($sql, 3600);
0079   
0080          while ($row = $db->sql_fetchrow($result))
0081          {
0082              $this->question_ids[$row['question_id']] = $row['question_id'];
0083          }
0084          $db->sql_freeresult($result);
0085   
0086          // fallback to the board default lang
0087          if (!count($this->question_ids))
0088          {
0089              $this->question_lang = $config['default_lang'];
0090   
0091              $sql = 'SELECT question_id
0092                  FROM ' . $this->table_captcha_questions . "
0093                  WHERE lang_iso = '" . $db->sql_escape($config['default_lang']) . "'";
0094              $result = $db->sql_query($sql, 7200);
0095   
0096              while ($row = $db->sql_fetchrow($result))
0097              {
0098                  $this->question_ids[$row['question_id']] = $row['question_id'];
0099              }
0100              $db->sql_freeresult($result);
0101          }
0102   
0103          // final fallback to any language
0104          if (!count($this->question_ids))
0105          {
0106              $this->question_lang = '';
0107   
0108              $sql = 'SELECT q.question_id, q.lang_iso
0109                  FROM ' . $this->table_captcha_questions . ' q, ' . $this->table_captcha_answers . ' a
0110                  WHERE q.question_id = a.question_id';
0111              $result = $db->sql_query($sql, 7200);
0112   
0113              while ($row = $db->sql_fetchrow($result))
0114              {
0115                  if (empty($this->question_lang))
0116                  {
0117                      $this->question_lang = $row['lang_iso'];
0118                  }
0119                  $this->question_ids[$row['question_id']] = $row['question_id'];
0120              }
0121              $db->sql_freeresult($result);
0122          }
0123   
0124          // okay, if there is a confirm_id, we try to load that confirm's state. If not, we try to find one
0125          if (!$this->load_answer() && (!$this->load_confirm_id() || !$this->load_answer()))
0126          {
0127              // we have no valid confirm ID, better get ready to ask something
0128              $this->select_question();
0129          }
0130      }
0131   
0132      /**
0133      * See if the captcha has created its tables.
0134      */
0135      public function is_installed()
0136      {
0137          global $phpbb_container;
0138   
0139          $db_tool = $phpbb_container->get('dbal.tools');
0140   
0141          return $db_tool->sql_table_exists($this->table_captcha_questions);
0142      }
0143   
0144      /**
0145      *  API function - for the captcha to be available, it must have installed itself and there has to be at least one question in the board's default lang
0146      */
0147      public function is_available()
0148      {
0149          global $config, $db, $user;
0150   
0151          // load language file for pretty display in the ACP dropdown
0152          $user->add_lang('captcha_qa');
0153   
0154          if (!$this->is_installed())
0155          {
0156              return false;
0157          }
0158   
0159          $sql = 'SELECT COUNT(question_id) AS question_count
0160              FROM ' . $this->table_captcha_questions . "
0161              WHERE lang_iso = '" . $db->sql_escape($config['default_lang']) . "'";
0162          $result = $db->sql_query($sql);
0163          $row = $db->sql_fetchrow($result);
0164          $db->sql_freeresult($result);
0165   
0166          return ((bool) $row['question_count']);
0167      }
0168   
0169      /**
0170      *  API function
0171      */
0172      function has_config()
0173      {
0174          return true;
0175      }
0176   
0177      /**
0178      *  API function
0179      */
0180      static public function get_name()
0181      {
0182          return 'CAPTCHA_QA';
0183      }
0184   
0185      /**
0186      * @return string the name of the service corresponding to the plugin
0187      */
0188      function get_service_name()
0189      {
0190          return $this->service_name;
0191      }
0192   
0193      /**
0194      * Set the name of the plugin
0195      *
0196      * @param string $name
0197      */
0198      public function set_name($name)
0199      {
0200          $this->service_name = $name;
0201      }
0202   
0203      /**
0204      *  API function - not needed as we don't display an image
0205      */
0206      function execute_demo()
0207      {
0208      }
0209   
0210      /**
0211      *  API function - not needed as we don't display an image
0212      */
0213      function execute()
0214      {
0215      }
0216   
0217      /**
0218      *  API function - send the question to the template
0219      */
0220      function get_template()
0221      {
0222          global $phpbb_log, $template, $user;
0223   
0224          if ($this->is_solved())
0225          {
0226              return false;
0227          }
0228          else if (empty($this->question_text) || !count($this->question_ids))
0229          {
0230              /** @var \phpbb\log\log_interface $phpbb_log */
0231              $phpbb_log->add('critical', $user->data['user_id'], $user->ip, 'LOG_ERROR_CAPTCHA', time(), array($user->lang('CONFIRM_QUESTION_MISSING')));
0232              return false;
0233          }
0234          else
0235          {
0236              $template->assign_vars(array(
0237                  'QA_CONFIRM_QUESTION'    => $this->question_text,
0238                  'QA_CONFIRM_ID'            => $this->confirm_id,
0239                  'S_CONFIRM_CODE'        => true,
0240                  'S_TYPE'                => $this->type,
0241              ));
0242   
0243              return 'captcha_qa.html';
0244          }
0245      }
0246   
0247      /**
0248      *  API function - we just display a mockup so that the captcha doesn't need to be installed
0249      */
0250      function get_demo_template()
0251      {
0252          global $config, $db, $template;
0253   
0254          if ($this->is_available())
0255          {
0256              $sql = 'SELECT question_text
0257                  FROM ' . $this->table_captcha_questions . "
0258                  WHERE lang_iso = '" . $db->sql_escape($config['default_lang']) . "'";
0259              $result = $db->sql_query_limit($sql, 1);
0260              if ($row = $db->sql_fetchrow($result))
0261              {
0262                  $template->assign_vars(array(
0263                      'QA_CONFIRM_QUESTION'        => $row['question_text'],
0264                  ));
0265              }
0266              $db->sql_freeresult($result);
0267          }
0268          return 'captcha_qa_acp_demo.html';
0269      }
0270   
0271      /**
0272      *  API function
0273      */
0274      function get_hidden_fields()
0275      {
0276          $hidden_fields = array();
0277   
0278          // this is required - otherwise we would forget about the captcha being already solved
0279          if ($this->solved)
0280          {
0281              $hidden_fields['qa_answer'] = $this->answer;
0282          }
0283          $hidden_fields['qa_confirm_id'] = $this->confirm_id;
0284   
0285          return $hidden_fields;
0286      }
0287   
0288      /**
0289      *  API function
0290      */
0291      function garbage_collect($type = 0)
0292      {
0293          global $db;
0294   
0295          $sql = 'SELECT c.confirm_id
0296              FROM ' . $this->table_qa_confirm . ' c
0297              LEFT JOIN ' . SESSIONS_TABLE . ' s
0298                  ON (c.session_id = s.session_id)
0299              WHERE s.session_id IS NULL' .
0300                  ((empty($type)) ? '' : ' AND c.confirm_type = ' . (int) $type);
0301          $result = $db->sql_query($sql);
0302   
0303          if ($row = $db->sql_fetchrow($result))
0304          {
0305              $sql_in = array();
0306   
0307              do
0308              {
0309                  $sql_in[] = (string) $row['confirm_id'];
0310              }
0311              while ($row = $db->sql_fetchrow($result));
0312   
0313              if (count($sql_in))
0314              {
0315                  $sql = 'DELETE FROM ' . $this->table_qa_confirm . '
0316                      WHERE ' . $db->sql_in_set('confirm_id', $sql_in);
0317                  $db->sql_query($sql);
0318              }
0319          }
0320          $db->sql_freeresult($result);
0321      }
0322   
0323      /**
0324      *  API function - we don't drop the tables here, as that would cause the loss of all entered questions.
0325      */
0326      function uninstall()
0327      {
0328          $this->garbage_collect(0);
0329      }
0330   
0331      /**
0332      *  API function - set up shop
0333      */
0334      function install()
0335      {
0336          global $phpbb_container;
0337   
0338          $db_tool = $phpbb_container->get('dbal.tools');
0339          $schemas = array(
0340                  $this->table_captcha_questions        => array (
0341                      'COLUMNS' => array(
0342                          'question_id'    => array('UINT', null, 'auto_increment'),
0343                          'strict'        => array('BOOL', 0),
0344                          'lang_id'        => array('UINT', 0),
0345                          'lang_iso'        => array('VCHAR:30', ''),
0346                          'question_text'    => array('TEXT_UNI', ''),
0347                      ),
0348                      'PRIMARY_KEY'        => 'question_id',
0349                      'KEYS'                => array(
0350                          'lang'            => array('INDEX', 'lang_iso'),
0351                      ),
0352                  ),
0353                  $this->table_captcha_answers        => array (
0354                      'COLUMNS' => array(
0355                          'question_id'    => array('UINT', 0),
0356                          'answer_text'    => array('STEXT_UNI', ''),
0357                      ),
0358                      'KEYS'                => array(
0359                          'qid'            => array('INDEX', 'question_id'),
0360                      ),
0361                  ),
0362                  $this->table_qa_confirm        => array (
0363                      'COLUMNS' => array(
0364                          'session_id'    => array('CHAR:32', ''),
0365                          'confirm_id'    => array('CHAR:32', ''),
0366                          'lang_iso'        => array('VCHAR:30', ''),
0367                          'question_id'    => array('UINT', 0),
0368                          'attempts'        => array('UINT', 0),
0369                          'confirm_type'    => array('USINT', 0),
0370                      ),
0371                      'KEYS'                => array(
0372                          'session_id'            => array('INDEX', 'session_id'),
0373                          'lookup'                => array('INDEX', array('confirm_id', 'session_id', 'lang_iso')),
0374                      ),
0375                      'PRIMARY_KEY'        => 'confirm_id',
0376                  ),
0377          );
0378   
0379          foreach ($schemas as $table => $schema)
0380          {
0381              if (!$db_tool->sql_table_exists($table))
0382              {
0383                  $db_tool->sql_create_table($table, $schema);
0384              }
0385          }
0386      }
0387   
0388      /**
0389      *  API function - see what has to be done to validate
0390      */
0391      function validate()
0392      {
0393          global $phpbb_log, $user;
0394   
0395          $error = '';
0396   
0397          if (!count($this->question_ids))
0398          {
0399              /** @var \phpbb\log\log_interface $phpbb_log */
0400              $phpbb_log->add('critical', $user->data['user_id'], $user->ip, 'LOG_ERROR_CAPTCHA', time(), array($user->lang('CONFIRM_QUESTION_MISSING')));
0401              return $user->lang('CONFIRM_QUESTION_MISSING');
0402          }
0403   
0404          if (!$this->confirm_id)
0405          {
0406              $error = $user->lang['CONFIRM_QUESTION_WRONG'];
0407          }
0408          else
0409          {
0410              if ($this->check_answer())
0411              {
0412                  $this->solved = true;
0413              }
0414              else
0415              {
0416                  $error = $user->lang['CONFIRM_QUESTION_WRONG'];
0417              }
0418          }
0419   
0420          if (strlen($error))
0421          {
0422              // okay, incorrect answer. Let's ask a new question.
0423              $this->new_attempt();
0424              $this->solved = false;
0425   
0426              return $error;
0427          }
0428          else
0429          {
0430              return false;
0431          }
0432      }
0433   
0434      /**
0435      *  Select a question
0436      */
0437      function select_question()
0438      {
0439          global $db, $user;
0440   
0441          if (!count($this->question_ids))
0442          {
0443              return;
0444          }
0445          $this->confirm_id = md5(unique_id($user->ip));
0446          $this->question = (int) array_rand($this->question_ids);
0447   
0448          $sql = 'INSERT INTO ' . $this->table_qa_confirm . ' ' . $db->sql_build_array('INSERT', array(
0449              'confirm_id'    => (string) $this->confirm_id,
0450              'session_id'    => (string) $user->session_id,
0451              'lang_iso'        => (string) $this->question_lang,
0452              'confirm_type'    => (int) $this->type,
0453              'question_id'    => (int) $this->question,
0454          ));
0455          $db->sql_query($sql);
0456   
0457          $this->load_answer();
0458      }
0459   
0460      /**
0461      * New Question, if desired.
0462      */
0463      function reselect_question()
0464      {
0465          global $db, $user;
0466   
0467          if (!count($this->question_ids))
0468          {
0469              return;
0470          }
0471   
0472          $this->question = (int) array_rand($this->question_ids);
0473          $this->solved = 0;
0474   
0475          $sql = 'UPDATE ' . $this->table_qa_confirm . '
0476              SET question_id = ' . (int) $this->question . "
0477              WHERE confirm_id = '" . $db->sql_escape($this->confirm_id) . "'
0478                  AND session_id = '" . $db->sql_escape($user->session_id) . "'";
0479          $db->sql_query($sql);
0480   
0481          $this->load_answer();
0482      }
0483   
0484      /**
0485      * Wrong answer, so we increase the attempts and use a different question.
0486      */
0487      function new_attempt()
0488      {
0489          global $db, $user;
0490   
0491          // yah, I would prefer a stronger rand, but this should work
0492          $this->question = (int) array_rand($this->question_ids);
0493          $this->solved = 0;
0494   
0495          $sql = 'UPDATE ' . $this->table_qa_confirm . '
0496              SET question_id = ' . (int) $this->question . ",
0497                  attempts = attempts + 1
0498              WHERE confirm_id = '" . $db->sql_escape($this->confirm_id) . "'
0499                  AND session_id = '" . $db->sql_escape($user->session_id) . "'";
0500          $db->sql_query($sql);
0501   
0502          $this->load_answer();
0503      }
0504   
0505   
0506      /**
0507      * See if there is already an entry for the current session.
0508      */
0509      function load_confirm_id()
0510      {
0511          global $db, $user;
0512   
0513          $sql = 'SELECT confirm_id
0514              FROM ' . $this->table_qa_confirm . "
0515              WHERE
0516                  session_id = '" . $db->sql_escape($user->session_id) . "'
0517                  AND lang_iso = '" . $db->sql_escape($this->question_lang) . "'
0518                  AND confirm_type = " . $this->type;
0519          $result = $db->sql_query_limit($sql, 1);
0520          $row = $db->sql_fetchrow($result);
0521          $db->sql_freeresult($result);
0522   
0523          if ($row)
0524          {
0525              $this->confirm_id = $row['confirm_id'];
0526              return true;
0527          }
0528          return false;
0529      }
0530   
0531      /**
0532      * Look up everything we need and populate the instance variables.
0533      */
0534      function load_answer()
0535      {
0536          global $db, $user;
0537   
0538          if (!strlen($this->confirm_id) || !count($this->question_ids))
0539          {
0540              return false;
0541          }
0542   
0543          $sql = 'SELECT con.question_id, attempts, question_text, strict
0544              FROM ' . $this->table_qa_confirm . ' con, ' . $this->table_captcha_questions . " qes
0545              WHERE con.question_id = qes.question_id
0546                  AND confirm_id = '" . $db->sql_escape($this->confirm_id) . "'
0547                  AND session_id = '" . $db->sql_escape($user->session_id) . "'
0548                  AND qes.lang_iso = '" . $db->sql_escape($this->question_lang) . "'
0549                  AND confirm_type = " . $this->type;
0550          $result = $db->sql_query($sql);
0551          $row = $db->sql_fetchrow($result);
0552          $db->sql_freeresult($result);
0553   
0554          if ($row)
0555          {
0556              $this->question = $row['question_id'];
0557   
0558              $this->attempts = $row['attempts'];
0559              $this->question_strict = $row['strict'];
0560              $this->question_text = $row['question_text'];
0561   
0562              return true;
0563          }
0564   
0565          return false;
0566      }
0567   
0568      /**
0569      *  The actual validation
0570      */
0571      function check_answer()
0572      {
0573          global $db, $request;
0574   
0575          $answer = ($this->question_strict) ? $request->variable('qa_answer', '', true) : utf8_clean_string($request->variable('qa_answer', '', true));
0576          $confirm_id = $request->variable('qa_confirm_id', '');
0577   
0578          $sql = 'SELECT answer_text
0579              FROM ' . $this->table_captcha_answers . '
0580              WHERE question_id = ' . (int) $this->question;
0581          $result = $db->sql_query($sql);
0582   
0583          while ($row = $db->sql_fetchrow($result))
0584          {
0585              $solution = ($this->question_strict) ? $row['answer_text'] : utf8_clean_string($row['answer_text']);
0586   
0587              if ($solution === $answer && $this->confirm_id === $confirm_id)
0588              {
0589                  $this->solved = true;
0590   
0591                  break;
0592              }
0593          }
0594          $db->sql_freeresult($result);
0595   
0596          return $this->solved;
0597      }
0598   
0599      /**
0600      *  API function
0601      */
0602      function get_attempt_count()
0603      {
0604          return $this->attempts;
0605      }
0606   
0607      /**
0608      *  API function
0609      */
0610      function reset()
0611      {
0612          global $db, $user;
0613   
0614          $sql = 'DELETE FROM ' . $this->table_qa_confirm . "
0615              WHERE session_id = '" . $db->sql_escape($user->session_id) . "'
0616                  AND confirm_type = " . (int) $this->type;
0617          $db->sql_query($sql);
0618   
0619          // we leave the class usable by generating a new question
0620          $this->select_question();
0621      }
0622   
0623      /**
0624      *  API function
0625      */
0626      function is_solved()
0627      {
0628          global $request;
0629   
0630          if ($request->variable('qa_answer', false) && $this->solved === 0)
0631          {
0632              $this->validate();
0633          }
0634   
0635          return (bool) $this->solved;
0636      }
0637   
0638      /**
0639      *  API function - The ACP backend, this marks the end of the easy methods
0640      */
0641      function acp_page($id, $module)
0642      {
0643          global $config, $request, $phpbb_log, $template, $user;
0644   
0645          $user->add_lang('acp/board');
0646          $user->add_lang('captcha_qa');
0647   
0648          if (!self::is_installed())
0649          {
0650              $this->install();
0651          }
0652   
0653          $module->tpl_name = 'captcha_qa_acp';
0654          $module->page_title = 'ACP_VC_SETTINGS';
0655          $form_key = 'acp_captcha';
0656          add_form_key($form_key);
0657   
0658          $submit = $request->variable('submit', false);
0659          $question_id = $request->variable('question_id', 0);
0660          $action = $request->variable('action', '');
0661   
0662          // we have two pages, so users might want to navigate from one to the other
0663          $list_url = $module->u_action . "&amp;configure=1&amp;select_captcha=" . $this->get_service_name();
0664   
0665          $template->assign_vars(array(
0666              'U_ACTION'        => $module->u_action,
0667              'QUESTION_ID'    => $question_id ,
0668              'CLASS'            => $this->get_service_name(),
0669          ));
0670   
0671          // show the list?
0672          if (!$question_id && $action != 'add')
0673          {
0674              $this->acp_question_list($module);
0675          }
0676          else if ($question_id && $action == 'delete')
0677          {
0678              if ($this->get_service_name() !== $config['captcha_plugin'] || !$this->acp_is_last($question_id))
0679              {
0680                  if (confirm_box(true))
0681                  {
0682                      $this->acp_delete_question($question_id);
0683   
0684                      trigger_error($user->lang['QUESTION_DELETED'] . adm_back_link($list_url));
0685                  }
0686                  else
0687                  {
0688                      confirm_box(false, $user->lang['CONFIRM_OPERATION'], build_hidden_fields(array(
0689                          'question_id'        => $question_id,
0690                          'action'            => $action,
0691                          'configure'            => 1,
0692                          'select_captcha'    => $this->get_service_name(),
0693                          ))
0694                      );
0695                  }
0696              }
0697              else
0698              {
0699                  trigger_error($user->lang['QA_LAST_QUESTION'] . adm_back_link($list_url), E_USER_WARNING);
0700              }
0701          }
0702          else
0703          {
0704              // okay, show the editor
0705              $question_input = $this->acp_get_question_input();
0706              $langs = $this->get_languages();
0707   
0708              foreach ($langs as $lang => $entry)
0709              {
0710                  $template->assign_block_vars('langs', array(
0711                      'ISO' => $lang,
0712                      'NAME' => $entry['name'],
0713                  ));
0714              }
0715   
0716              $template->assign_vars(array(
0717                  'U_LIST' => $list_url,
0718              ));
0719   
0720              if ($question_id)
0721              {
0722                  if ($question = $this->acp_get_question_data($question_id))
0723                  {
0724                      $template->assign_vars(array(
0725                          'QUESTION_TEXT'        => ($question_input['question_text']) ? $question_input['question_text'] : $question['question_text'],
0726                          'LANG_ISO'            => ($question_input['lang_iso']) ? $question_input['lang_iso'] : $question['lang_iso'],
0727                          'STRICT'            => (isset($_REQUEST['strict'])) ? $question_input['strict'] : $question['strict'],
0728                          'ANSWERS'            => implode("\n", $question['answers']),
0729                      ));
0730                  }
0731                  else
0732                  {
0733                      trigger_error($user->lang['FORM_INVALID'] . adm_back_link($list_url));
0734                  }
0735              }
0736              else
0737              {
0738                  $template->assign_vars(array(
0739                      'QUESTION_TEXT'        => $question_input['question_text'],
0740                      'LANG_ISO'            => $question_input['lang_iso'],
0741                      'STRICT'            => $question_input['strict'],
0742                      'ANSWERS'            => (is_array($question_input['answers'])) ? implode("\n", $question_input['answers']) : '',
0743                  ));
0744              }
0745   
0746              if ($submit && check_form_key($form_key))
0747              {
0748                  if (!$this->validate_input($question_input))
0749                  {
0750                      $template->assign_vars(array(
0751                          'S_ERROR'            => true,
0752                      ));
0753                  }
0754                  else
0755                  {
0756                      if ($question_id)
0757                      {
0758                          $this->acp_update_question($question_input, $question_id);
0759                      }
0760                      else
0761                      {
0762                          $this->acp_add_question($question_input);
0763                      }
0764   
0765                      $phpbb_log->add('admin', $user->data['user_id'], $user->ip, 'LOG_CONFIG_VISUAL');
0766                      trigger_error($user->lang['CONFIG_UPDATED'] . adm_back_link($list_url));
0767                  }
0768              }
0769              else if ($submit)
0770              {
0771                  trigger_error($user->lang['FORM_INVALID'] . adm_back_link($list_url), E_USER_WARNING);
0772              }
0773          }
0774      }
0775   
0776      /**
0777      *  This handles the list overview
0778      */
0779      function acp_question_list($module)
0780      {
0781          global $db, $template;
0782   
0783          $sql = 'SELECT *
0784              FROM ' . $this->table_captcha_questions;
0785          $result = $db->sql_query($sql);
0786   
0787          $template->assign_vars(array(
0788              'S_LIST'            => true,
0789          ));
0790   
0791          while ($row = $db->sql_fetchrow($result))
0792          {
0793              $url = $module->u_action . "&amp;question_id={$row['question_id']}&amp;configure=1&amp;select_captcha=" . $this->get_service_name() . '&amp;';
0794   
0795              $template->assign_block_vars('questions', array(
0796                  'QUESTION_TEXT'        => $row['question_text'],
0797                  'QUESTION_ID'        => $row['question_id'],
0798                  'QUESTION_LANG'        => $row['lang_iso'],
0799                  'U_DELETE'            => "{$url}action=delete",
0800                  'U_EDIT'            => "{$url}action=edit",
0801              ));
0802          }
0803          $db->sql_freeresult($result);
0804      }
0805   
0806      /**
0807      *  Grab a question and bring it into a format the editor understands
0808      */
0809      function acp_get_question_data($question_id)
0810      {
0811          global $db;
0812   
0813          if ($question_id)
0814          {
0815              $sql = 'SELECT *
0816                  FROM ' . $this->table_captcha_questions . '
0817                  WHERE question_id = ' . $question_id;
0818              $result = $db->sql_query($sql);
0819              $question = $db->sql_fetchrow($result);
0820              $db->sql_freeresult($result);
0821   
0822              if (!$question)
0823              {
0824                  return false;
0825              }
0826   
0827              $question['answers'] = array();
0828   
0829              $sql = 'SELECT *
0830                  FROM ' . $this->table_captcha_answers . '
0831                  WHERE question_id = ' . $question_id;
0832              $result = $db->sql_query($sql);
0833   
0834              while ($row = $db->sql_fetchrow($result))
0835              {
0836                  $question['answers'][] = $row['answer_text'];
0837              }
0838              $db->sql_freeresult($result);
0839   
0840              return $question;
0841          }
0842   
0843          return false;
0844      }
0845   
0846      /**
0847      *  Grab a question from input and bring it into a format the editor understands
0848      */
0849      function acp_get_question_input()
0850      {
0851          global $request;
0852   
0853          $answers = $request->variable('answers', '', true);
0854   
0855          // Convert answers into array and filter if answers are set
0856          if (strlen($answers))
0857          {
0858              $answers = array_filter(array_map('trim', explode("\n", $answers)), function ($value) {
0859                  return $value !== '';
0860              });
0861          }
0862   
0863          $question = array(
0864              'question_text'    => $request->variable('question_text', '', true),
0865              'strict'        => $request->variable('strict', false),
0866              'lang_iso'        => $request->variable('lang_iso', ''),
0867              'answers'        => $answers,
0868          );
0869          return $question;
0870      }
0871   
0872      /**
0873      *  Update a question.
0874      * param mixed $data : an array as created from acp_get_question_input or acp_get_question_data
0875      */
0876      function acp_update_question($data, $question_id)
0877      {
0878          global $db, $cache;
0879   
0880          // easier to delete all answers than to figure out which to update
0881          $sql = 'DELETE FROM ' . $this->table_captcha_answers . " WHERE question_id = $question_id";
0882          $db->sql_query($sql);
0883   
0884          $langs = $this->get_languages();
0885          $question_ary = $data;
0886          $question_ary['lang_id'] = $langs[$question_ary['lang_iso']]['id'];
0887          unset($question_ary['answers']);
0888   
0889          $sql = 'UPDATE ' . $this->table_captcha_questions . '
0890              SET ' . $db->sql_build_array('UPDATE', $question_ary) . "
0891              WHERE question_id = $question_id";
0892          $db->sql_query($sql);
0893   
0894          $this->acp_insert_answers($data, $question_id);
0895   
0896          $cache->destroy('sql', $this->table_captcha_questions);
0897      }
0898   
0899      /**
0900      *  Insert a question.
0901      * param mixed $data : an array as created from acp_get_question_input or acp_get_question_data
0902      */
0903      function acp_add_question($data)
0904      {
0905          global $db, $cache;
0906   
0907          $langs = $this->get_languages();
0908          $question_ary = $data;
0909   
0910          $question_ary['lang_id'] = $langs[$data['lang_iso']]['id'];
0911          unset($question_ary['answers']);
0912   
0913          $sql = 'INSERT INTO ' . $this->table_captcha_questions . ' ' . $db->sql_build_array('INSERT', $question_ary);
0914          $db->sql_query($sql);
0915   
0916          $question_id = $db->sql_nextid();
0917   
0918          $this->acp_insert_answers($data, $question_id);
0919   
0920          $cache->destroy('sql', $this->table_captcha_questions);
0921      }
0922   
0923      /**
0924      *  Insert the answers.
0925      * param mixed $data : an array as created from acp_get_question_input or acp_get_question_data
0926      */
0927      function acp_insert_answers($data, $question_id)
0928      {
0929          global $db, $cache;
0930   
0931          foreach ($data['answers'] as $answer)
0932          {
0933              $answer_ary = array(
0934                  'question_id'    => $question_id,
0935                  'answer_text'    => $answer,
0936              );
0937   
0938              $sql = 'INSERT INTO ' . $this->table_captcha_answers . ' ' . $db->sql_build_array('INSERT', $answer_ary);
0939              $db->sql_query($sql);
0940          }
0941   
0942          $cache->destroy('sql', $this->table_captcha_answers);
0943      }
0944   
0945      /**
0946      *  Delete a question.
0947      */
0948      function acp_delete_question($question_id)
0949      {
0950          global $db, $cache;
0951   
0952          $tables = array($this->table_captcha_questions, $this->table_captcha_answers);
0953   
0954          foreach ($tables as $table)
0955          {
0956              $sql = "DELETE FROM $table
0957                  WHERE question_id = $question_id";
0958              $db->sql_query($sql);
0959          }
0960   
0961          $cache->destroy('sql', $tables);
0962      }
0963   
0964      /**
0965      *  Check if the entered data can be inserted/used
0966      * param mixed $data : an array as created from acp_get_question_input or acp_get_question_data
0967      */
0968      function validate_input($question_data)
0969      {
0970          $langs = $this->get_languages();
0971   
0972          if (!isset($question_data['lang_iso']) ||
0973              !isset($question_data['question_text']) ||
0974              !isset($question_data['strict']) ||
0975              !isset($question_data['answers']))
0976          {
0977              return false;
0978          }
0979   
0980          if (!isset($langs[$question_data['lang_iso']]) ||
0981              !strlen($question_data['question_text']) ||
0982              !count($question_data['answers']) ||
0983              !is_array($question_data['answers']))
0984          {
0985              return false;
0986          }
0987   
0988          return true;
0989      }
0990   
0991      /**
0992      * List the installed language packs
0993      */
0994      function get_languages()
0995      {
0996          global $db;
0997   
0998          $sql = 'SELECT *
0999              FROM ' . LANG_TABLE;
1000          $result = $db->sql_query($sql);
1001   
1002          $langs = array();
1003          while ($row = $db->sql_fetchrow($result))
1004          {
1005              $langs[$row['lang_iso']] = array(
1006                  'name'    => $row['lang_local_name'],
1007                  'id'    => (int) $row['lang_id'],
1008              );
1009          }
1010          $db->sql_freeresult($result);
1011   
1012          return $langs;
1013      }
1014   
1015   
1016   
1017      /**
1018      *  See if there is a question other than the one we have
1019      */
1020      function acp_is_last($question_id)
1021      {
1022          global $config, $db;
1023   
1024          if ($question_id)
1025          {
1026              $sql = 'SELECT question_id
1027                  FROM ' . $this->table_captcha_questions . "
1028                  WHERE lang_iso = '" . $db->sql_escape($config['default_lang']) . "'
1029                      AND  question_id <> " .  (int) $question_id;
1030              $result = $db->sql_query_limit($sql, 1);
1031              $question = $db->sql_fetchrow($result);
1032              $db->sql_freeresult($result);
1033   
1034              if (!$question)
1035              {
1036                  return true;
1037              }
1038              return false;
1039          }
1040      }
1041  }
1042