Save the items in the menus, converting from JS structure to real menu links.

@param string $menuId
@param mixed[] $items
@param Site $site
486952
protected function saveMenuItems(Menu $menu, $items)
    {
        $itemStorage  = $this->treeManager->getItemStorage();
        $currentTree  = $this->treeManager->buildTree($menu->getId(), false);
        $menuId       = $menu->getId();

        // First, get all elements so that we can delete those that are removed
        // @todo pri: sorry this is inneficient, but I need it
        $old = [];
        foreach ($currentTree->getAll() as $item) {
            $old[$item->getId()] = $item;
        }

        // FIXME, this is coming from javascript, we should really check access on nodes

        // Keep a list of processed elements
        $processed = [];
        $deleteItems = [];

        // Keep in mind that items ordered
        if (!empty($items)) {

            // Because they are ordered and a parent will be saved before a child,
            // thus modifying a child: we have to use a classic loop
            $itemsCount = count($items);

            // Keep track of latest root parent found, in order to reorder top
            // level item relative to each other.
            $latestRootParentId = null;

            for ($i = 0; $i < $itemsCount; $i++) {

                $item     = $items[$i];
                $nodeId   = $item['item_id'];
                $isNew    = substr($item['id'], 0, 4) == 'new_' || empty($item['id']);
                $title    = trim($item['title']);
                $itemId   = $isNew ? null : $item['id'];
                $parentId = empty($item['parent_id']) ? null : $item['parent_id'];

                if ($isNew) {
                    if ($parentId) {
                        $itemId = $itemStorage->insertAsChild($parentId, $nodeId, $title);
                    } else {
                        $itemId = $itemStorage->insert($menuId, $nodeId, $title);
                        // Move the new item, in root tree, right after the
                        // lastest processed root item
                        if ($latestRootParentId) {
                            $itemStorage->moveAfter($itemId, $latestRootParentId);
                        }
                    }
                    // New potential parent item inserted, replace potential children parent_id
                    foreach ($items as $index => $potentialChild) {
                        if ($potentialChild['parent_id'] === $item['id']) {
                            $items[$index]['parent_id'] = $itemId;
                        }
                    }
                } else {
                    if ($parentId) {
                        $itemStorage->moveAsChild($itemId, $parentId);
                    } else {
                        if ($latestRootParentId) {
                            $itemStorage->moveAfter($itemId, $latestRootParentId);
                        } else {
                            $itemStorage->moveToRoot($itemId);
                        }
                    }

                    // When inserting or moving an item in the root of the tree
                    // we must keep track of it for being able to properly place
                    // the item relatively to its previous sibling.
                    if (!$parentId) {
                        $latestRootParentId = $itemId;
                    }

                    // Update title if revelant
                    if ($title !== $currentTree->getItemById($itemId)->getTitle()) {
                        $itemStorage->update($itemId, null, $title);
                    }
                }

                $processed[$itemId] = true;
            }
        }

        $newTree = $this->treeManager->buildTree($menuId, false, false, true);

        // Remove elements not in the original array
        foreach (array_diff_key($old, $processed) as $itemId => $deleted) {
            $itemStorage->delete($itemId);
            $deleteItems[$itemId] = $deleted;
        }

        $this->dispatcher->dispatch(
            MenuEvent::EVENT_TREE,
            new MenuEvent(
                $menu->getName(),
                $newTree,
                $deleteItems,
                $menu->hasSiteId() ? $this->siteManager->getStorage()->findOne($menu->getSiteId()) : null
            )
        );
    }
1302591
public static function addToMenu(MenuInterface $menu, array $items): void
    {
        $normalisedItems = self::normaliseItems($items);
        foreach ($normalisedItems as $item) {
            $menu->addMenuItem(MenuItemFactory::createMenuItem($item));
        }
    }
1373760
protected function saveToCache(array $items, string $fileName): void
    {
        $content       = serialize($items);
        $directoryName = dirname($fileName);
        if (!is_dir($directoryName)) {
            mkdir($directoryName, $this->cacheFileMode, true);
        }
        file_put_contents($fileName, $content, LOCK_EX);
        // For cache files we don't want executable bits by default
        chmod($fileName, $this->cacheFileMode & (~0111));
    }
60567
private function cloneTreeRecursion(int $menuId, TreeBase $item, $parentId = null)
    {
        if ($item->hasChildren()) {

            $previous = null;
            foreach ($item->getChildren() as $child) {

                if ($previous) {
                    $previous = $this->itemStorage->insertAfter($previous, $child->getNodeId(), $child->getTitle(), $child->getDescription());
                } else if ($parentId) {
                    $previous = $this->itemStorage->insertAsChild($parentId, $child->getNodeId(), $child->getTitle(), $child->getDescription());
                } else {
                    $previous = $this->itemStorage->insert($menuId, $child->getNodeId(), $child->getTitle(), $child->getDescription());
                }

                $this->cloneTreeRecursion($menuId, $child, $previous);
            }
        }
    }
390296
public function deleteItem( $id )
	{
		$siteid = $this->_getContext()->getLocale()->getSiteId();
		$this->begin();

		try
		{
			$this->_createTreeManager( $siteid )->deleteNode( $id );
			$this->commit();
		}
		catch( Exception $e )
		{
			$this->rollback();
			throw $e;
		}
	}
734444
public function saveSiteAction()
    {
        $request = $this->getRequest();
        $siteId = $request->getPost('site_id', null);
        $status  = 0;
        $errors  = array();
        $textMessage = '';
        $textTitle = '';
        
        $this->getEventManager()->trigger('meliscms_site_save_start', $this, array());
        
        $translator = $this->getServiceLocator()->get('translator');
        $melisTool = $this->getServiceLocator()->get('MelisCoreTool');
        $melisTool->setMelisToolKey(self::TOOL_INDEX, self::TOOL_KEY);
       
        // Getting the Creation/Edition Site Form
        if (is_null($siteId))
        {
            $siteFormType = 'meliscms_site_tool_creation_form';
            $logTypeCode = 'CMS_SITE_ADD';
        }
        else
        {
            $siteFormType = 'meliscms_site_tool_edition_form';
            $logTypeCode = 'CMS_SITE_UPDATE';
        }
        
        $siteForm = $melisTool->getForm($siteFormType);
        
        if($request->isPost()) 
        {
            $siteTable = $this->getServiceLocator()->get('MelisEngineTableSite');
            $domainTable = $this->getServiceLocator()->get('MelisEngineTableSiteDomain');
            $site404Table = $this->getServiceLocator()->get('MelisEngineTableSite404');
            
            $postValues = get_object_vars($request->getPost());
            $postValues = $melisTool->sanitizePost($postValues);
            $siteForm->setData($postValues);
            
            
            
            if($siteForm->isValid()) 
            {
                $data = $siteForm->getData();
                
                $dataSite = array();
                $dataDomain = array();
                $dataSite404 = array();
                $dataSiteLang= null;
                
                if (!empty($data['plang_lang_id'])){
                    $dataSiteLang = $data['plang_lang_id'];
                }
                
                foreach($data as $dataKey => $dataValue) 
                {
                    if(strpos($dataKey, self::SITE_TABLE_PREFIX) !== false) {
                        $dataSite[$dataKey] = $data[$dataKey];
                    }
                    
                    if(strpos($dataKey, self::SITE404_TABLE_PREFIX) !== false) {
                        $dataSite404[$dataKey] = $data[$dataKey];
                    }
                    
                    if(strpos($dataKey, self::DOMAIN_TABLE_PREFIX) !== false) {
                        $dataDomain[$dataKey] = $data[$dataKey];
                    }
                }
                
                $isValidName = false;
                // Checking if the Site name is existing on the site lists
                $siteName = $this->getRequest()->getPost('site_name');
                $siteData = $siteTable->getEntryByField('site_name', $siteName)->current();
                
                if (is_null($siteId))
                {
                    if (empty($siteData))
                    {
                        $isValidName = true;
                    }
                }
                else 
                {
                    if (!empty($siteData))
                    {
                        
                        if ($siteData->site_id == $siteId)
                        {
                            $isValidName = true;
                        }
                    }
                    else 
                    {
                        $isValidName = true;
                    }
                }
                
                if($isValidName) 
                {
                    $genSiteModule = !empty($request->getPost('gen_site_mod')) ? true : false;
                    
                    $cmsSiteSrv = $this->getServiceLocator()->get('MelisCmsSiteService');
                    $saveSiteResult = $cmsSiteSrv->saveSite($dataSite, $dataDomain, $dataSite404, $dataSiteLang, $siteId, $genSiteModule, $dataSite['site_name']);
                    
                    if ($saveSiteResult['success'])
                    {
                        if (is_null($siteId))
                        {
                            $textMessage = 'tr_meliscms_tool_site_add_success';
                        }
                        else 
                        {
                            $textMessage = 'tr_meliscms_tool_site_edit_success';
                        }
                        
                        $siteId = $saveSiteResult['site_id'];
                        // to solve the error on loading css and js
                        // regenerate the melis.modules.path cause the newly created path is not
                        // included in the file
                        $this->regenerateModulesPath();
                        $status = 1;
                    }
                    else 
                    {
                        if ($saveSiteResult['message'] == 'tr_meliscms_tool_site_no_platform_ids')
                        {
                            $textMessage = 'tr_meliscms_tool_site_error_prompt_add';
                            $errors['platformId'] = array(
                                'label' => $translator->translate('tr_meliscms_tool_platform_ids'),
                                'noPlatformIds' => $translator->translate('tr_meliscms_tool_site_no_platform_ids')
                            );
                        }
                        else 
                        {
                            $textMessage = $saveSiteResult['message'];
                        }
                    }
                    
                    unset($data);
                    unset($dataSite);
                    unset($dataSite404);
                    unset($dataDomain);
                }
                else 
                {
                    // if data already exists then add manually the foreign site id key
                    $textMessage = 'tr_meliscms_tool_site_error_prompt_add';
                    $errors = array(
                        'site_name' => array(
                            'siteAlreadyExists' => $translator->translate('tr_meliscms_tool_site_name_exists')
                        ),
                    );
                }
            }
            else 
            {
                if (is_null($siteId))
                {
                    $textMessage = 'tr_meliscms_tool_site_error_prompt_add';
                }
                else
                {
                    $textMessage = 'tr_meliscms_tool_site_edit_failed';
                }
                
                $errors = $siteForm->getMessages();
                $status = 0;
            }
            
            // insert labels and error messages in error array
            $melisMelisCoreConfig = $this->serviceLocator->get('MelisCoreConfig');
            $appConfigForm = $melisMelisCoreConfig->getItem('meliscms/tools/meliscms_tool_site/forms/'.$siteFormType);
            $appConfigForm = $appConfigForm['elements'];
            
            foreach ($errors as $keyError => $valueError)
            {
                foreach ($appConfigForm as $keyForm => $valueForm)
                {
                    if ($valueForm['spec']['name'] == $keyError && !empty($valueForm['spec']['options']['label']))
                    {
                        $errors[$keyError]['label'] = $valueForm['spec']['options']['label'];
                    }
                }
            }
        }
        
        $response = array(
            'success' => $status,
            'textTitle' => 'tr_meliscms_tool_site',
            'textMessage' => $textMessage,
            'errors' => $errors,
            'test' => $errors,
        );

        $this->getEventManager()->trigger('meliscms_site_save_end', $this, array_merge($response, array('typeCode' => $logTypeCode, 'itemId' => $siteId)));
        
        if ($siteId)
        {
            $response['siteId'] = $siteId;
        }
        
        return new JsonModel($response);
    }
871578
public function mergeMenus(array $items)
    {
        foreach ($items as $item) {
            $menu = Yii::createObject($item['menu']);
            $this->mergeItems($menu->getItems());
        }
    }
604936
protected void onSiteChange(String site) {

        try {
            m_treeData.removeAllItems();
            CmsObject rootCms = OpenCms.initCmsObject(A_CmsUI.getCmsObject());
            rootCms.getRequestContext().setSiteRoot("");
            CmsResource siteRootResource = rootCms.readResource(site);

            m_treeData.initRoot(rootCms, siteRootResource);
            m_fileTree.expandItem(siteRootResource.getStructureId());
            m_siteRoot = site;
            updateRoot(rootCms, siteRootResource);
        } catch (CmsException e) {
            LOG.error(e.getLocalizedMessage(), e);
        }
    }
1156859
public function addWebmasters(Site $site, $userIdList)
    {
        $this->mergeUsersWithRole($site, $userIdList, Access::ROLE_WEBMASTER);
    }
452964
function menuChildren(items, $children, counter) {
    if (!counter) {
        counter = 0;
    }
    
    $children.each(function() {
        var $node = $(this),
            node = this,
            nodeName = this.nodeName.toLowerCase(),
            label,
            item;
        
        // extract <label><input>
        if (nodeName == 'label' && $node.find('input, textarea, select').length) {
            label = $node.text();
            $node = $node.children().first();
            node = $node.get(0);
            nodeName = node.nodeName.toLowerCase();
        }
        
        /*
         * <menu> accepts flow-content as children. that means <embed>, <canvas> and such are valid menu items.
         * Not being the sadistic kind, $.contextMenu only accepts:
         * <command>, <menuitem>, <hr>, <span>, <p> <input [text, radio, checkbox]>, <textarea>, <select> and of course <menu>.
         * Everything else will be imported as an html node, which is not interfaced with contextMenu.
         */
        
        // http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#concept-command
        switch (nodeName) {
            // http://www.whatwg.org/specs/web-apps/current-work/multipage/interactive-elements.html#the-menu-element
            case 'menu':
                item = {name: $node.attr('label'), items: {}};
                counter = menuChildren(item.items, $node.children(), counter);
                break;
            
            // http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#using-the-a-element-to-define-a-command
            case 'a':
            // http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#using-the-button-element-to-define-a-command
            case 'button':
                item = {
                    name: $node.text(),
                    disabled: !!$node.attr('disabled'),
                    callback: (function(){ return function(){ $node.click(); }; })()
                };
                break;
            
            // http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#using-the-command-element-to-define-a-command

            case 'menuitem':
            case 'command':
                switch ($node.attr('type')) {
                    case undefined:
                    case 'command':
                    case 'menuitem':
                        item = {
                            name: $node.attr('label'),
                            disabled: !!$node.attr('disabled'),
                            callback: (function(){ return function(){ $node.click(); }; })()
                        };
                        break;
                        
                    case 'checkbox':
                        item = {
                            type: 'checkbox',
                            disabled: !!$node.attr('disabled'),
                            name: $node.attr('label'),
                            selected: !!$node.attr('checked')
                        };
                        break;
                        
                    case 'radio':
                        item = {
                            type: 'radio',
                            disabled: !!$node.attr('disabled'),
                            name: $node.attr('label'),
                            radio: $node.attr('radiogroup'),
                            value: $node.attr('id'),
                            selected: !!$node.attr('checked')
                        };
                        break;
                        
                    default:
                        item = undefined;
                }
                break;
 
            case 'hr':
                item = '-------';
                break;
                
            case 'input':
                switch ($node.attr('type')) {
                    case 'text':
                        item = {
                            type: 'text',
                            name: label || inputLabel(node),
                            disabled: !!$node.attr('disabled'),
                            value: $node.val()
                        };
                        break;
                        
                    case 'checkbox':
                        item = {
                            type: 'checkbox',
                            name: label || inputLabel(node),
                            disabled: !!$node.attr('disabled'),
                            selected: !!$node.attr('checked')
                        };
                        break;
                        
                    case 'radio':
                        item = {
                            type: 'radio',
                            name: label || inputLabel(node),
                            disabled: !!$node.attr('disabled'),
                            radio: !!$node.attr('name'),
                            value: $node.val(),
                            selected: !!$node.attr('checked')
                        };
                        break;
                    
                    default:
                        item = undefined;
                        break;
                }
                break;
                
            case 'select':
                item = {
                    type: 'select',
                    name: label || inputLabel(node),
                    disabled: !!$node.attr('disabled'),
                    selected: $node.val(),
                    options: {}
                };
                $node.children().each(function(){
                    item.options[this.value] = $(this).text();
                });
                break;
                
            case 'textarea':
                item = {
                    type: 'textarea',
                    name: label || inputLabel(node),
                    disabled: !!$node.attr('disabled'),
                    value: $node.val()
                };
                break;
            
            case 'label':
                break;
            
            default:
                item = {type: 'html', html: $node.clone(true)};
                break;
        }
        
        if (item) {
            counter++;
            items['key' + counter] = item;
        }
    });
    
    return counter;
}
272701
public function deleteReferenceBulkFromSite($siteId, $nodeIdList)
    {
        if (!$nodeIdList) {
            return;
        }

        $this
            ->db
            ->delete('ucms_site_node')
            ->condition('nid', $nodeIdList)
            ->condition('site_id', $siteId)
            ->execute()
        ;

        $this
            ->entityManager
            ->getStorage('node')
            ->resetCache($nodeIdList)
        ;

        $this->eventDispatcher->dispatch(NodeEvents::ACCESS_CHANGE, new ResourceEvent('node', $nodeIdList));
        $this->eventDispatcher->dispatch(SiteEvents::EVENT_DETACH, new SiteAttachEvent($siteId, $nodeIdList));
    }
17964
public function set($agentId, $menus)
    {
        $menus = $this->extractMenus($menus);

        $this->http->jsonPost(self::API_CREATE.'?agentid='.$agentId, array('button' => $menus));

        return true;
    }
1214140
protected function generateCoreSelectorMenu(Site $site, string $uriToRedirectTo = null)
    {
        if (!$this->view instanceof BackendTemplateView) {
            throw new InvalidViewObjectNameException(vsprintf(
                'The controller "%s" must use BackendTemplateView to be able to generate menu for backends docheader. \
                Please set `protected $defaultViewObjectName = BackendTemplateView::class;` field in your controller.',
                [static::class]), 1493804179);
        }
        $this->view->getModuleTemplate()->setFlashMessageQueue($this->controllerContext->getFlashMessageQueue());

        $this->coreSelectorMenu = $this->view->getModuleTemplate()->getDocHeaderComponent()->getMenuRegistry()->makeMenu();
        $this->coreSelectorMenu->setIdentifier('component_core_selector_menu');

        if (!isset($uriToRedirectTo)) {
            $uriToRedirectTo = $this->uriBuilder->reset()->uriFor();
        }

        $this->initializeSelectedSolrCoreConnection();
        $cores = $this->solrConnectionManager->getConnectionsBySite($site);
        foreach ($cores as $core) {
            $coreAdmin = $core->getAdminService();
            $menuItem = $this->coreSelectorMenu->makeMenuItem();
            $menuItem->setTitle($coreAdmin->getCorePath());
            $uri = $this->uriBuilder->reset()->uriFor('switchCore',
                [
                    'corePath' => $coreAdmin->getCorePath(),
                    'uriToRedirectTo' => $uriToRedirectTo
                ]
            );
            $menuItem->setHref($uri);

            if ($coreAdmin->getCorePath() == $this->selectedSolrCoreConnection->getAdminService()->getCorePath()) {
                $menuItem->setActive(true);
            }
            $this->coreSelectorMenu->addMenuItem($menuItem);
        }

        $this->view->getModuleTemplate()->getDocHeaderComponent()->getMenuRegistry()->addMenu($this->coreSelectorMenu);
    }
294765
public function createReferenceBulkInSite($siteId, $nodeIdList)
    {
        // @todo Optimize me
        foreach ($nodeIdList as $nodeId) {
            $this
                ->db
                ->merge('ucms_site_node')
                ->key(['nid' => $nodeId, 'site_id' => $siteId])
                ->execute()
            ;
        }

        $this
            ->entityManager
            ->getStorage('node')
            ->resetCache($nodeIdList)
        ;

        $this->eventDispatcher->dispatch(NodeEvents::ACCESS_CHANGE, new ResourceEvent('node', $nodeIdList));
        $this->eventDispatcher->dispatch(SiteEvents::EVENT_ATTACH, new SiteAttachEvent($siteId, $nodeIdList));
    }
1326338
protected function writeSiteAliases($site_id, array $aliases) {

    if (!is_dir($this->siteAliasDir)) {
      mkdir($this->siteAliasDir);
    }
    $filePath = $this->siteAliasDir . '/' . $site_id . '.site.yml';
    if (file_exists($filePath)) {
      if (!$this->confirm("File $filePath already exists and will be overwritten. Continue?")) {
        throw new \Exception("Aborted at user request");
      }
    }

    file_put_contents($filePath, Yaml::dump($aliases));
    return $filePath;
  }
666550
public function addDropdown($items, $fx, $icon = 'caret square down', $menuId = null)
    {
        $menuITems = [];
        foreach ($items as $key => $item) {
            if (is_int($key)) {
                $menuITems[] = ['name' => $item, 'value' => $item];
            } else {
                $menuITems[] = ['name' => $key, 'value' => $item];
            }
        }

        $cb = $this->setHeaderDropdown($menuITems, $icon, $menuId);

        $cb->onSelectItem(function ($menu, $item) use ($fx) {
            return call_user_func($fx, $item, $menu);
        });
    }
408060
public function onMenuTree(MenuEvent $event)
    {
        $refresh = [];

        foreach ($event->getDeletedItems() as $item) {
            $refresh[] = $item->getNodeId();
        }
        foreach ($event->getTree()->getChildren() as $item) {
            $refresh[] = $item->getNodeId();
        }

        if ($refresh) {
            $this->service->onAliasChange($refresh);
        }
    }