1. This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.

How do I render a $view with createTemplateObject?

Discussion in 'XenForo Development Discussions' started by Jaxel, Dec 25, 2010.

  1. Jaxel

    Jaxel Well-Known Member

    As you know... I have XenPorta... The way it works, is that it collects the module information, and then builds the modules into HTML so that it can be embedded in the portal page...

    Code:
    <?php
    
    class EWRporta_ViewPublic_Portal extends XenForo_ViewPublic_Base
    {
        public function renderHtml()
        {
            $blocks = array('top' => array(), 'bottom' => array(), 'left' => array(), 'center' => array(), 'right' => array());
    
            foreach ($this->_params['modules'] AS $module)
            {
                switch ($module['module_position'])
                {
                    case "top":        $blocks['top'][] = $this->createTemplateObject($module['template'], $module['params']);        break;
                    case "bottom":    $blocks['bottom'][] = $this->createTemplateObject($module['template'], $module['params']);    break;
                    case "left":    $blocks['left'][] = $this->createTemplateObject($module['template'], $module['params']);    break;
                    case "center":    $blocks['center'][] = $this->createTemplateObject($module['template'], $module['params']);    break;
                    case "right":    $blocks['right'][] = $this->createTemplateObject($module['template'], $module['params']);    break;
                }
            }
    
            $this->_params['blocks'] = $blocks;
        }
    }
    The issue, is that since these createTemplateObject call are embedded into an existing $view, they don't have a $view of their own. While in most cases, this is perfectly fine, it creates issues with my Recent News Module Block and custom BBcode that require templates. For instance, using the QUOTE tag... it uses a !$view fallback, which doesn't look anywhere near as good as the $view version.

    How would I get it so that these advanced BBcodes that require templates are processed in full, instead of the fallback?
     
  2. Shadab

    Shadab Well-Known Member

    From what I understand, you want the modules to have a separate View class ?
    (inheriting from XenForo_ViewPublic_Base)

    You'd need a View renderer object for that.
    One is available as a protected member field ($_renderer) so you can try reusing it.

    PHP:
    $content $this->_renderer->renderView($module['view_class'], $module['params'], $module['template']);
     
  3. Jaxel

    Jaxel Well-Known Member

    Okay... so I tried:
    Code:
    $object = $this->_renderer->renderView($module['module_name'], $module['params'], $module['template']);
    instead of:
    Code:
    $object = $this->createTemplateObject($module['template'], $module['params']);
    Unfortunately, this still doesn't render advanced BBcodes like QUOTE and CODE.
     
  4. Shadab

    Shadab Well-Known Member

    Where are you rendering the bbcodes and how? If you are already rendering it from inside a view, no matter which, you can pass the current view object to the bbcode formatter:
    PHP:
    $bbCodeFormatter XenForo_BbCode_Formatter_Base::create('Base', array('view' => $this));
    $bbCodeParser = new XenForo_BbCode_Parser($bbCodeFormatter);

    Btw, could you point out the files from your addon (I haven't installed yet) responsible for rendering a module and a sample module which exhibits this behavior? I could then take a closer look and find a solution; instead of just taking educated guesses without checking the actual source! :)
     
  5. Jaxel

    Jaxel Well-Known Member

    The BBcode is rendered in a model:
    Code:
    <?php
    
    class EWRporta_Block_RecentNews extends XenForo_Model
    {
    	public function getModule($options)
    	{
    		$news = $this->_getDb()->fetchAll("
    			SELECT xf_thread.*, xf_user.*, xf_post.message, xf_post.post_id, xf_node.title AS node_title
    				FROM xf_thread
    				LEFT JOIN xf_user ON (xf_user.user_id = xf_thread.user_id)
    				LEFT JOIN xf_post ON (xf_post.post_id = xf_thread.first_post_id)
    				LEFT JOIN xf_node ON (xf_node.node_id = xf_thread.node_id)
    			WHERE xf_thread.node_id IN (".$options['recentnews_forum'].")
    				AND xf_thread.discussion_state = 'visible'
    			ORDER BY xf_thread.post_date DESC
    			LIMIT ?
    		", $options['recentnews_limit']);
    
    		foreach ($news AS &$post)
    		{
    			$bbCodeParser = new XenForo_BbCode_Parser(XenForo_BbCode_Formatter_Base::create());
    
    			$post = $this->getThumb($post);
    			$post['HTML'] = preg_replace('#\n{3,}#', "\n\n", $post['message']);
    			$post['HTML'] = XenForo_Helper_String::autoLinkBbCode($post['HTML']);
    
    			if (preg_match('#(.+)\[prebreak\].*?\[/prebreak\]#si', $post['HTML'], $matches))
    			{
    				$post['HTML'] = trim($matches[1]);
    			}
    			else
    			{
    				$post['HTML'] = substr($post['HTML'], 0, $options['recentnews_truncate']);
    			}
    
    			$post['HTML'] = $bbCodeParser->render($post['HTML']);
    		}
    
            return $news;
    	}
    
    	public function getThumb($post)
    	{
    		$attachments = $this->getModelFromCache('XenForo_Model_Attachment')->getAttachmentsByContentId('post', $post['post_id']);
    
    		foreach ($attachments AS $attach)
    		{
    			$attach = $this->getModelFromCache('XenForo_Model_Attachment')->prepareAttachment($attach);
    
    			if ($attach['thumbnailUrl'])
    			{
    				$post['attach'] = $attach;
    				$post['message'] = preg_replace('#\[attach[\w=]*\]'.$attach['attachment_id'].'\[/attach\]#i', '', $post['message']);
    				return $post;
    			}
    		}
    
    		if (preg_match('#\[medio\](\d+)\[/medio\]#i', $post['message'], $matches))
    		{
    			if ($post['medio'] = $this->getModelFromCache('EWRmedio_Model_Media')->getMediaByID($matches[1]))
    			{
    				$post['message'] = preg_replace('#\[medio\]'.$matches[1].'\[/medio\]#i', '', $post['message']);
    				return $post;
    			}
    		}
    
    		if (preg_match('#\[img\](.+?)\[/img\]#i', $post['message'], $matches))
    		{
    			$post['image'] = $matches[1];
    			$post['message'] = preg_replace('#\[img\]'.$matches[1].'\[/img\]#i', '', $post['message']);
    			return $post;
    		}
    
    		return $post;
    	}
    }
     
  6. Mike

    Mike XenForo Developer Staff Member

    Rendering BB code is not a model behavior - it needs to be done in the view, as it is view/display logic. Shadab's explanation is correct and you won't be able to properly render BB code until the code is refactored to that.
     
  7. Jaxel

    Jaxel Well-Known Member

    Then I'm at a complete loss on how to do this... since the modules are modularized, there is no real way to embed them directly into the view...

    Code:
    <?php
    
    class EWRporta_ViewPublic_Portal extends XenForo_ViewPublic_Base
    {
    	public function renderHtml()
    	{
    		$modulesModel = new EWRporta_Model_Modules;
    		$settingsModel = new EWRporta_Model_Settings;
    		$cacheModel = new EWRporta_Model_Cache;
    
    		$modules = $modulesModel->getModules();
    		$settings = $settingsModel->getSettings();
    		$blocks = array('top' => array(), 'bottom' => array(), 'left' => array(), 'center' => array(), 'right' => array());
    
    		foreach ($modules AS $module)
    		{
    			$template = 'EWRporta_Block_'.$module['module_name'];
    			$cache = $cacheModel->getCacheByName($module['module_name']);
    			$params = array('option' => array());
    
    			if (strtotime($module['module_cache'], $cache['module_date']) < time())
    			{
    				if (!empty($settings[$module['module_name']]))
    				{
    					$params['option'] = $settings[$module['module_name']];
    				}
    
    				if (XenForo_Application::autoload($template))
    				{
    					$model = new $template;
    					$params[$module['module_name']] = $model->getModule($params['option']);
    				}
    
    				$cacheModel->buildModuleCache($module, serialize($params));
    			}
    			else
    			{
    				$params = unserialize($cache['module_settings']);
    			}
    
    			$object = $this->createTemplateObject($template, $params);
    
    			switch ($module['module_position'])
    			{
    				case "top":			$blocks['top'][] = $object;			break;
    				case "bottom":		$blocks['bottom'][] = $object;		break;
    				case "left":		$blocks['left'][] = $object;		break;
    				case "center":		$blocks['center'][] = $object;		break;
    				case "right":		$blocks['right'][] = $object;		break;
    			}
    		}
    
    		$this->_params['blocks'] = $blocks;
    	}
    }
     
  8. Jaxel

    Jaxel Well-Known Member

    I have tried passing the view as $this into the module, but that didnt work because it faisl the XFCP check:
    Code:
    $params[$module['module_name']] = $model->getModule($params['option'], $this);
     
  9. Jaxel

    Jaxel Well-Known Member

    Okay... I think I came up with a clever solution... in the actual module model, I put the following:
    Code:
    $options['arrayBB'] = true;
    or
    Code:
    $options['parseBB'] = true;
    Then I simply added the following within my view class...
    Code:
    if (!empty($params['option']['parseBB']))
    {
    	$bbCodeParser = new XenForo_BbCode_Parser(XenForo_BbCode_Formatter_Base::create('Base', array('view' => $this)));
    	$params[$module['module_name']]['HTML'] = new XenForo_BbCode_TextWrapper($params[$module['module_name']]['HTML'], $bbCodeParser);
    }
    else if (!empty($params['option']['arrayBB']))
    {
    	foreach ($params[$module['module_name']] AS &$item)
    	{
    		$bbCodeParser = new XenForo_BbCode_Parser(XenForo_BbCode_Formatter_Base::create('Base', array('view' => $this)));
    		$item['HTML'] = new XenForo_BbCode_TextWrapper($item['HTML'], $bbCodeParser);
    	}
    }
    Basically, the mod just tells the view parser whether or not it needs BBcode parsed... it can even handle an array of data if the option is set. Of course, it will only parse data in the HTML index; so it needs to be controlled. But this worked!
     
  10. Jaxel

    Jaxel Well-Known Member

    Hmm... I found an issue...
    http://www.8wayrun.com/portal/

    Scroll down the last recent news post and you will see the following:
    Thats odd isn't it? It parses the QUOTE and the CODE tags without issue; but then it treats the ATTACH as it doesn't have a $view? Is there a reason why this happens? I've determined it has something to do with "$rendererStates", but I have no idea how that stuff works.
     
  11. Shadab

    Shadab Well-Known Member

    Look at how it's done in XenForo_ViewPublic_Thread_View::renderHtml()
    Check the attachment viewing permission in a controller, and pass it as a bbcode option via "viewAttachments" key.
     
  12. Jaxel

    Jaxel Well-Known Member

    Interesting... I tried the following:
    Code:
    $bbCodeParser = new XenForo_BbCode_Parser(XenForo_BbCode_Formatter_Base::create('Base', array('view' => $this)));
    $bbCodeOptions = array(
    	'states' => array(
    		'viewAttachments' => true
    	)
    );
    XenForo_ViewPublic_Helper_Message::bbCodeWrapMessages($params[$module['module_name']], $bbCodeParser, $bbCodeOptions);
    Still doesnt work... still getting "View Attachment 3211"

    the "viewAttachments" state only tells whether a user can view an attachment or not... Its still failing the "attachment" state.
     
  13. Jaxel

    Jaxel Well-Known Member

    I got it... I satisfied the "attachment" state by preparing my attachments in the module itself:
    Code:
    foreach ($this->getModelFromCache('XenForo_Model_Attachment')->getAttachmentsByContentId('post', $post['post_id']) AS $attachmentId => $attachment)
    {
    	$post['attachments'][$attachmentId] = $this->getModelFromCache('XenForo_Model_Attachment')->prepareAttachment($attachment);
    }
     

Share This Page