"Fossies" - the Fresh Open Source Software Archive

Member "contao-4.4.48/core-bundle/src/Resources/contao/classes/Ajax.php" (2 Apr 2020, 14617 Bytes) of package /linux/www/contao-4.4.48.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) PHP source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "Ajax.php" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 4.8.8_vs_4.9.0.

    1 <?php
    2 
    3 /*
    4  * This file is part of Contao.
    5  *
    6  * (c) Leo Feyer
    7  *
    8  * @license LGPL-3.0-or-later
    9  */
   10 
   11 namespace Contao;
   12 
   13 use Contao\CoreBundle\Exception\InternalServerErrorHttpException;
   14 use Contao\CoreBundle\Exception\NoContentResponseException;
   15 use Contao\CoreBundle\Exception\ResponseException;
   16 use Symfony\Component\HttpFoundation\Response;
   17 use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBagInterface;
   18 use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
   19 
   20 /**
   21  * Provide methods to handle Ajax requests.
   22  *
   23  * @author Leo Feyer <https://github.com/leofeyer>
   24  */
   25 class Ajax extends \Backend
   26 {
   27     /**
   28      * Ajax action
   29      * @var string
   30      */
   31     protected $strAction;
   32 
   33     /**
   34      * Ajax id
   35      * @var string
   36      */
   37     protected $strAjaxId;
   38 
   39     /**
   40      * Ajax key
   41      * @var string
   42      */
   43     protected $strAjaxKey;
   44 
   45     /**
   46      * Ajax name
   47      * @var string
   48      */
   49     protected $strAjaxName;
   50 
   51     /**
   52      * Get the current action
   53      *
   54      * @param string $strAction
   55      *
   56      * @throws \Exception
   57      */
   58     public function __construct($strAction)
   59     {
   60         if ($strAction == '')
   61         {
   62             throw new \Exception('Missing Ajax action');
   63         }
   64 
   65         $this->strAction = $strAction;
   66         parent::__construct();
   67     }
   68 
   69     /**
   70      * Ajax actions that do not require a data container object
   71      */
   72     public function executePreActions()
   73     {
   74         /** @var AttributeBagInterface $objSessionBag */
   75         $objSessionBag = \System::getContainer()->get('session')->getBag('contao_backend');
   76 
   77         switch ($this->strAction)
   78         {
   79             // Toggle navigation menu
   80             case 'toggleNavigation':
   81                 $bemod = $objSessionBag->get('backend_modules');
   82                 $bemod[\Input::post('id')] = (int) \Input::post('state');
   83                 $objSessionBag->set('backend_modules', $bemod);
   84 
   85                 throw new NoContentResponseException();
   86 
   87             // Load a navigation menu group
   88             case 'loadNavigation':
   89                 $bemod = $objSessionBag->get('backend_modules');
   90                 $bemod[\Input::post('id')] = (int) \Input::post('state');
   91                 $objSessionBag->set('backend_modules', $bemod);
   92 
   93                 $this->import('BackendUser', 'User');
   94 
   95                 /** @var BackendTemplate|object $objTemplate */
   96                 $objTemplate = new \BackendTemplate('be_navigation');
   97                 $navigation = $this->User->navigation();
   98                 $objTemplate->modules = $navigation[\Input::post('id')]['modules'];
   99 
  100                 throw new ResponseException($objTemplate->getResponse());
  101 
  102             // Toggle nodes of the file or page tree
  103             case 'toggleStructure':
  104             case 'toggleFileManager':
  105             case 'togglePagetree':
  106             case 'toggleFiletree':
  107                 $this->strAjaxId = preg_replace('/.*_([0-9a-zA-Z]+)$/', '$1', \Input::post('id'));
  108                 $this->strAjaxKey = str_replace('_' . $this->strAjaxId, '', \Input::post('id'));
  109 
  110                 if (\Input::get('act') == 'editAll')
  111                 {
  112                     $this->strAjaxKey = preg_replace('/(.*)_[0-9a-zA-Z]+$/', '$1', $this->strAjaxKey);
  113                     $this->strAjaxName = preg_replace('/.*_([0-9a-zA-Z]+)$/', '$1', \Input::post('name'));
  114                 }
  115 
  116                 $nodes = $objSessionBag->get($this->strAjaxKey);
  117                 $nodes[$this->strAjaxId] = (int) \Input::post('state');
  118                 $objSessionBag->set($this->strAjaxKey, $nodes);
  119 
  120                 throw new NoContentResponseException();
  121 
  122             // Load nodes of the file or page tree
  123             case 'loadStructure':
  124             case 'loadFileManager':
  125             case 'loadPagetree':
  126             case 'loadFiletree':
  127                 $this->strAjaxId = preg_replace('/.*_([0-9a-zA-Z]+)$/', '$1', \Input::post('id'));
  128                 $this->strAjaxKey = str_replace('_' . $this->strAjaxId, '', \Input::post('id'));
  129 
  130                 if (\Input::get('act') == 'editAll')
  131                 {
  132                     $this->strAjaxKey = preg_replace('/(.*)_[0-9a-zA-Z]+$/', '$1', $this->strAjaxKey);
  133                     $this->strAjaxName = preg_replace('/.*_([0-9a-zA-Z]+)$/', '$1', \Input::post('name'));
  134                 }
  135 
  136                 $nodes = $objSessionBag->get($this->strAjaxKey);
  137                 $nodes[$this->strAjaxId] = (int) \Input::post('state');
  138                 $objSessionBag->set($this->strAjaxKey, $nodes);
  139                 break;
  140 
  141             // Toggle the visibility of a fieldset
  142             case 'toggleFieldset':
  143                 $fs = $objSessionBag->get('fieldset_states');
  144                 $fs[\Input::post('table')][\Input::post('id')] = (int) \Input::post('state');
  145                 $objSessionBag->set('fieldset_states', $fs);
  146 
  147                 throw new NoContentResponseException();
  148 
  149             // Toggle checkbox groups
  150             case 'toggleCheckboxGroup':
  151                 $state = $objSessionBag->get('checkbox_groups');
  152                 $state[\Input::post('id')] = (int) \Input::post('state');
  153                 $objSessionBag->set('checkbox_groups', $state);
  154                 break;
  155 
  156             // HOOK: pass unknown actions to callback functions
  157             default:
  158                 if (isset($GLOBALS['TL_HOOKS']['executePreActions']) && \is_array($GLOBALS['TL_HOOKS']['executePreActions']))
  159                 {
  160                     foreach ($GLOBALS['TL_HOOKS']['executePreActions'] as $callback)
  161                     {
  162                         $this->import($callback[0]);
  163                         $this->{$callback[0]}->{$callback[1]}($this->strAction);
  164                     }
  165                 }
  166                 break;
  167         }
  168     }
  169 
  170     /**
  171      * Ajax actions that do require a data container object
  172      *
  173      * @param DataContainer $dc
  174      *
  175      * @throws NoContentResponseException
  176      * @throws ResponseException
  177      * @throws BadRequestHttpException
  178      */
  179     public function executePostActions(DataContainer $dc)
  180     {
  181         header('Content-Type: text/html; charset=' . \Config::get('characterSet'));
  182 
  183         // Bypass any core logic for non-core drivers (see #5957)
  184         if (!$dc instanceof DC_File && !$dc instanceof DC_Folder && !$dc instanceof DC_Table)
  185         {
  186             $this->executePostActionsHook($dc);
  187 
  188             throw new NoContentResponseException();
  189         }
  190 
  191         switch ($this->strAction)
  192         {
  193             // Load nodes of the page structure tree
  194             case 'loadStructure':
  195                 throw new ResponseException($this->convertToResponse($dc->ajaxTreeView($this->strAjaxId, (int) \Input::post('level'))));
  196 
  197             // Load nodes of the file manager tree
  198             case 'loadFileManager':
  199                 throw new ResponseException($this->convertToResponse($dc->ajaxTreeView(\Input::post('folder', true), (int) \Input::post('level'))));
  200 
  201             // Load nodes of the page tree
  202             case 'loadPagetree':
  203                 $varValue = null;
  204                 $strField = $dc->field = \Input::post('name');
  205 
  206                 // Call the load_callback
  207                 if (\is_array($GLOBALS['TL_DCA'][$dc->table]['fields'][$strField]['load_callback']))
  208                 {
  209                     foreach ($GLOBALS['TL_DCA'][$dc->table]['fields'][$strField]['load_callback'] as $callback)
  210                     {
  211                         if (\is_array($callback))
  212                         {
  213                             $this->import($callback[0]);
  214                             $varValue = $this->{$callback[0]}->{$callback[1]}($varValue, $dc);
  215                         }
  216                         elseif (\is_callable($callback))
  217                         {
  218                             $varValue = $callback($varValue, $dc);
  219                         }
  220                     }
  221                 }
  222 
  223                 /** @var PageSelector $strClass */
  224                 $strClass = $GLOBALS['BE_FFL']['pageSelector'];
  225 
  226                 /** @var PageSelector $objWidget */
  227                 $objWidget = new $strClass($strClass::getAttributesFromDca($GLOBALS['TL_DCA'][$dc->table]['fields'][$strField], $dc->field, $varValue, $strField, $dc->table, $dc));
  228 
  229                 throw new ResponseException($this->convertToResponse($objWidget->generateAjax($this->strAjaxId, \Input::post('field'), (int) \Input::post('level'))));
  230 
  231             // Load nodes of the file tree
  232             case 'loadFiletree':
  233                 $varValue = null;
  234                 $strField = $dc->field = \Input::post('name');
  235 
  236                 // Call the load_callback
  237                 if (\is_array($GLOBALS['TL_DCA'][$dc->table]['fields'][$strField]['load_callback']))
  238                 {
  239                     foreach ($GLOBALS['TL_DCA'][$dc->table]['fields'][$strField]['load_callback'] as $callback)
  240                     {
  241                         if (\is_array($callback))
  242                         {
  243                             $this->import($callback[0]);
  244                             $varValue = $this->{$callback[0]}->{$callback[1]}($varValue, $dc);
  245                         }
  246                         elseif (\is_callable($callback))
  247                         {
  248                             $varValue = $callback($varValue, $dc);
  249                         }
  250                     }
  251                 }
  252 
  253                 /** @var FileSelector $strClass */
  254                 $strClass = $GLOBALS['BE_FFL']['fileSelector'];
  255 
  256                 /** @var FileSelector $objWidget */
  257                 $objWidget = new $strClass($strClass::getAttributesFromDca($GLOBALS['TL_DCA'][$dc->table]['fields'][$strField], $dc->field, $varValue, $strField, $dc->table, $dc));
  258 
  259                 // Load a particular node
  260                 if (\Input::post('folder', true) != '')
  261                 {
  262                     throw new ResponseException($this->convertToResponse($objWidget->generateAjax(\Input::post('folder', true), \Input::post('field'), (int) \Input::post('level'))));
  263                 }
  264 
  265                 throw new ResponseException($this->convertToResponse($objWidget->generate()));
  266 
  267             // Reload the page/file picker
  268             case 'reloadPagetree':
  269             case 'reloadFiletree':
  270                 $intId = \Input::get('id');
  271                 $strField = $dc->inputName = \Input::post('name');
  272 
  273                 // Handle the keys in "edit multiple" mode
  274                 if (\Input::get('act') == 'editAll')
  275                 {
  276                     $intId = preg_replace('/.*_([0-9a-zA-Z]+)$/', '$1', $strField);
  277                     $strField = preg_replace('/(.*)_[0-9a-zA-Z]+$/', '$1', $strField);
  278                 }
  279 
  280                 $dc->field = $strField;
  281 
  282                 // The field does not exist
  283                 if (!isset($GLOBALS['TL_DCA'][$dc->table]['fields'][$strField]))
  284                 {
  285                     $this->log('Field "' . $strField . '" does not exist in DCA "' . $dc->table . '"', __METHOD__, TL_ERROR);
  286 
  287                     throw new BadRequestHttpException('Bad request');
  288                 }
  289 
  290                 $objRow = null;
  291                 $varValue = null;
  292 
  293                 // Load the value
  294                 if (\Input::get('act') != 'overrideAll')
  295                 {
  296                     if ($GLOBALS['TL_DCA'][$dc->table]['config']['dataContainer'] == 'File')
  297                     {
  298                         $varValue = \Config::get($strField);
  299                     }
  300                     elseif ($intId > 0 && $this->Database->tableExists($dc->table))
  301                     {
  302                         $objRow = $this->Database->prepare("SELECT * FROM " . $dc->table . " WHERE id=?")
  303                                                  ->execute($intId);
  304 
  305                         // The record does not exist
  306                         if ($objRow->numRows < 1)
  307                         {
  308                             $this->log('A record with the ID "' . $intId . '" does not exist in table "' . $dc->table . '"', __METHOD__, TL_ERROR);
  309 
  310                             throw new BadRequestHttpException('Bad request');
  311                         }
  312 
  313                         $varValue = $objRow->$strField;
  314                         $dc->activeRecord = $objRow;
  315                     }
  316                 }
  317 
  318                 // Call the load_callback
  319                 if (\is_array($GLOBALS['TL_DCA'][$dc->table]['fields'][$strField]['load_callback']))
  320                 {
  321                     foreach ($GLOBALS['TL_DCA'][$dc->table]['fields'][$strField]['load_callback'] as $callback)
  322                     {
  323                         if (\is_array($callback))
  324                         {
  325                             $this->import($callback[0]);
  326                             $varValue = $this->{$callback[0]}->{$callback[1]}($varValue, $dc);
  327                         }
  328                         elseif (\is_callable($callback))
  329                         {
  330                             $varValue = $callback($varValue, $dc);
  331                         }
  332                     }
  333                 }
  334 
  335                 // Set the new value
  336                 $varValue = \Input::post('value', true);
  337                 $strKey = ($this->strAction == 'reloadPagetree') ? 'pageTree' : 'fileTree';
  338 
  339                 // Convert the selected values
  340                 if ($varValue != '')
  341                 {
  342                     $varValue = \StringUtil::trimsplit("\t", $varValue);
  343 
  344                     // Automatically add resources to the DBAFS
  345                     if ($strKey == 'fileTree')
  346                     {
  347                         foreach ($varValue as $k=>$v)
  348                         {
  349                             $v = rawurldecode($v);
  350 
  351                             if (\Dbafs::shouldBeSynchronized($v))
  352                             {
  353                                 $objFile = \FilesModel::findByPath($v);
  354 
  355                                 if ($objFile === null)
  356                                 {
  357                                     $objFile = \Dbafs::addResource($v);
  358                                 }
  359 
  360                                 $varValue[$k] = $objFile->uuid;
  361                             }
  362                         }
  363                     }
  364 
  365                     $varValue = serialize($varValue);
  366                 }
  367 
  368                 /** @var FileTree|PageTree $strClass */
  369                 $strClass = $GLOBALS['BE_FFL'][$strKey];
  370 
  371                 /** @var FileTree|PageTree $objWidget */
  372                 $objWidget = new $strClass($strClass::getAttributesFromDca($GLOBALS['TL_DCA'][$dc->table]['fields'][$strField], $dc->inputName, $varValue, $strField, $dc->table, $dc));
  373 
  374                 throw new ResponseException($this->convertToResponse($objWidget->generate()));
  375 
  376             // Feature/unfeature an element
  377             case 'toggleFeatured':
  378                 if (class_exists($dc->table, false))
  379                 {
  380                     $dca = new $dc->table();
  381 
  382                     if (method_exists($dca, 'toggleFeatured'))
  383                     {
  384                         $dca->toggleFeatured(\Input::post('id'), ((\Input::post('state') == 1) ? true : false));
  385                     }
  386                 }
  387 
  388                 throw new NoContentResponseException();
  389 
  390             // Toggle subpalettes
  391             case 'toggleSubpalette':
  392                 $this->import('BackendUser', 'User');
  393 
  394                 // Check whether the field is a selector field and allowed for regular users (thanks to Fabian Mihailowitsch) (see #4427)
  395                 if (!\is_array($GLOBALS['TL_DCA'][$dc->table]['palettes']['__selector__']) || !\in_array(\Input::post('field'), $GLOBALS['TL_DCA'][$dc->table]['palettes']['__selector__']) || ($GLOBALS['TL_DCA'][$dc->table]['fields'][\Input::post('field')]['exclude'] && !$this->User->hasAccess($dc->table . '::' . \Input::post('field'), 'alexf')))
  396                 {
  397                     $this->log('Field "' . \Input::post('field') . '" is not an allowed selector field (possible SQL injection attempt)', __METHOD__, TL_ERROR);
  398 
  399                     throw new BadRequestHttpException('Bad request');
  400                 }
  401 
  402                 if ($dc instanceof DC_Table)
  403                 {
  404                     if (\Input::get('act') == 'editAll')
  405                     {
  406                         $this->strAjaxId = preg_replace('/.*_([0-9a-zA-Z]+)$/', '$1', \Input::post('id'));
  407                         $this->Database->prepare("UPDATE " . $dc->table . " SET " . \Input::post('field') . "='" . ((\Input::post('state') == 1) ? 1 : '') . "' WHERE id=?")->execute($this->strAjaxId);
  408 
  409                         if (\Input::post('load'))
  410                         {
  411                             throw new ResponseException($this->convertToResponse($dc->editAll($this->strAjaxId, \Input::post('id'))));
  412                         }
  413                     }
  414                     else
  415                     {
  416                         $this->Database->prepare("UPDATE " . $dc->table . " SET " . \Input::post('field') . "='" . ((\Input::post('state') == 1) ? 1 : '') . "' WHERE id=?")->execute($dc->id);
  417 
  418                         if (\Input::post('load'))
  419                         {
  420                             throw new ResponseException($this->convertToResponse($dc->edit(false, \Input::post('id'))));
  421                         }
  422                     }
  423                 }
  424                 elseif ($dc instanceof DC_File)
  425                 {
  426                     $val = ((\Input::post('state') == 1) ? true : false);
  427                     \Config::persist(\Input::post('field'), $val);
  428 
  429                     if (\Input::post('load'))
  430                     {
  431                         \Config::set(\Input::post('field'), $val);
  432 
  433                         throw new ResponseException($this->convertToResponse($dc->edit(false, \Input::post('id'))));
  434                     }
  435                 }
  436 
  437                 throw new NoContentResponseException();
  438 
  439             // DropZone file upload
  440             case 'fileupload':
  441                 $dc->move(true);
  442 
  443                 throw new InternalServerErrorHttpException();
  444 
  445             // HOOK: pass unknown actions to callback functions
  446             default:
  447                 $this->executePostActionsHook($dc);
  448 
  449                 throw new NoContentResponseException();
  450         }
  451     }
  452 
  453     /**
  454      * Execute the post actions hook
  455      *
  456      * @param DataContainer $dc
  457      */
  458     protected function executePostActionsHook(DataContainer $dc)
  459     {
  460         if (isset($GLOBALS['TL_HOOKS']['executePostActions']) && \is_array($GLOBALS['TL_HOOKS']['executePostActions']))
  461         {
  462             foreach ($GLOBALS['TL_HOOKS']['executePostActions'] as $callback)
  463             {
  464                 $this->import($callback[0]);
  465                 $this->{$callback[0]}->{$callback[1]}($this->strAction, $dc);
  466             }
  467         }
  468     }
  469 
  470     /**
  471      * Convert a string to a response object
  472      *
  473      * @param string $str
  474      *
  475      * @return Response
  476      */
  477     protected function convertToResponse($str)
  478     {
  479         return new Response(\Controller::replaceOldBePaths($str));
  480     }
  481 }