Chapter 3: Item Type

Chapter 3: Item Type

In the item.php file, we define our class as follows:

class ARKContextsMyCCKItem extends ARKContextsBase
{
	//Rest of the code goes here…
}

We call the class ARKContextsMyCCKItem and extend the ARKContextsBase class,  similar to what we did with the category type.

Remember the class name has to follow this rule: ARKContexts + [COMPONENT NAME/Context Name] + [Type].

So, for the Item type, for our MyCCK component the class name will be ARKContextsMyCCKItem.

So the whole code for the class, similar to the category class, is as follows:

class ARKContextsMyCCKItem extends ARKContextsBase
{
	public function __construct($id)
	{
		$this->table = 	JTable::getInstance('item','MyCCK');
		$this->table->load($id);
		parent::__construct($id);
	}	


    public function get()
	{
		if($this->id == null)
			return parent::get();	
		

		$this->table->articletext = $this->table->introtext;

		if (!empty($this->table->fulltext))
		{
			$this->table->articletext .= '<hr id="system-readmore" />' . $this->table->fulltext;
		}
	
		return parent::get();	
	}
	
	
	
	public function triggerContentPlugins($rawText)
	{
		
		$item = new stdclass;
					
		$text = '';
		
		if (isset($rawText))
		{
			$pattern = '#<hr\s+id=("|\')system-readmore("|\')\s*\/*>#i';
			$tagPos = preg_match($pattern, $rawText);
			
			if ($tagPos == 0)
			{
				
				$text = $rawText;
			}
			else
			{
				list ($text, $rawText) = preg_split($pattern, $rawText, 2);
				$text = $text.$rawText;
			}
		}

		$item->text = $text;
		$params = new JObject;
		$params->set('inline',false);
		$dispatcher	= JEventDispatcher::getInstance();
		JPluginHelper::importPlugin('content');
		$dispatcher->trigger('onContentPrepare', array ('com_mycck.item', &$item, &$params, 0));
			
		return array( 'data'=>$item->text);
	}
			
	public function save($data,$type = 'body')
	{
		if($this->id == null)
			return array( 'title'=>'','data'=>'');	
			
		if($type == 'body')
		{
			$data['articletext'] = base64_decode($data['text']);	
		}
		else	
		{
			$data['title'] = strip_tags($data['title']); 
		}
	
		
		$result = $this->table->save($data);
		//We need to process data as we are sending it back to the client
		
		JModelLegacy::addIncludePath(JPATH_SITE.'/components/com_mycck/models');
		$model = JModelLegacy::getInstance('item','MyCCKModel');
		$item = $model->getItem($this->id);
			
	
		if ($item->params->get('show_intro', '1') == '1')
		{
			$item->text = $item->introtext.' '.$item->fulltext;
		}
		elseif ($item->fulltext)
		{
			$item->text = $item->fulltext;
		}
		else
		{
			$item->text = $item->introtext;
		}
		
		//let's detect if any plugin tags are being used 
		//if so let's inform the system to warn the user
		$message = $this->detectPluginTags($item->text);
		
		$dispatcher	= JEventDispatcher::getInstance();
		JPluginHelper::importPlugin('content');
		$item->params->set('inline',false); //set this so inline plugin does not pick this up
		$dispatcher->trigger('onContentPrepare', array ('com_mycck.item', &$item, &$item->params, 0));
		
		return array( 'title'=>html_entity_decode($item->title),'data'=>$item->text,'message'=>$message);
	}
	
	public function version($versionId,$type)
	{
				
		$historyTable = JTable::getInstance('Contenthistory');
		$historyTable->load($versionId);
		$rowArray = JArrayHelper::fromObject(json_decode($historyTable->version_data));
			
		$item = $this->table;

		$item->bind($rowArray);	
		
		if($type == 'title')
		{
			return array( 'data'=>$item->title);
		}
		$text = '';
				
		$text = $item->introtext;
		if (!empty($item->fulltext))
		{
			$text .= '<hr id="system-readmore" />' . $item->fulltext;
		}
	
	
		return array( 'data'=>$text);
		
	}
}

As you can see in this class, the implementation of the code is similar to what we do in the category one. The constructors in the two are virtually identical: We use them to load up our JTable instance to get the current record from the database for our type.

The following methods, ‘get’, ‘triggerContentPlugins’, ‘save’ are also implemented with similar code logic as in the category class.

Now, in this part, where we go into further detail, we won't go into too much detail again, but only highlight the differences in this class implementation and the category one.

Read more: Chapter 3: Item Type

  • Hits: 13

Chapter 3: Category type

Chapter 3: Category type

In the category.php file, we define our class as follows:

class ARKContextsMyCCKCategory extends ARKContextsBase
{


}

Note: the class name is made up of the prefix ARKContents + [COMPONENT NAME/Context Name] + [Type]

 

Now, let us look at the constructor of this class:

public function __construct($id)
{
	JTable::addIncludePath(JPATH_ADMINISTRATOR.'/components/com_mycck/tables');
$this->table = JTable::getInstance('category',’MyCCK’);
	$this->table->load($id);	
	return parent::__construct($id);	
}

Firstly, in this constructor, we set up the table member variable, that holds the data from the database for the currently selected record.      

 

Now, let us take a look at the implementation of the get method:

public function get()
{
	if($this->id == null) //check if Id has been passed and bail if not!
		return parent::get();	

	$this->table->articletext = $this->table->description;

	return parent::get(); //call parent function always.	
}

In this function, we set up the ‘articletext’ property. This property or the ‘title’ are passed back to the parent get function. We only need to populate one, and the one you need depends on the type you are editing.  If the type is text only, such as a text input, you would only set the title property. If the type allows HTML, like a Textarea, then you would populate the ‘aritcletext’ property as in the above example.

 

The next function we need to implement is the triggerContentPlugins function, and this is the method we use when we want to render the content back to the user. In this function, we simply follow the rendering process we do in our component, calling any content plugins that are required.

So here is our implementation for the MyCCK content construction kit:

public function triggerContentPlugins($rawText)
{
	
	$item = new stdclass;
				
	$item->text = '';
	if(isset($rawText))
	{
		$item->text = $rawText;
		$params = new JObject;
		$params->set('inline',false); //set this so that we don't trigger the inline content plugins
		$dispatcher	= JEventDispatcher::getInstance();
		JPluginHelper::importPlugin('content');
		$dispatcher->trigger('onContentPrepare', array ('com_mycck.category', &$item, &$params, 0));
		
	}	
		
	return array( 'data'=>$item->text);
}

As you can see in the code, we do the standard Joomla code for firing off a plugin event. And in this case, it is the onContentPrepare event, similar to what happens when we render the category description for our component.

The important line here is this one:

$params->set('inline',false); //set this so that we don't trigger the inline content plugins

We need to set this parameter setting to ensure we do not call any inline content plugins again; in doing this we will ensure we will not be responsible for causing any self-conflicting issues.

The last function we need to implement for now, is the save function. In our inline app for our MyCCK component, we have implemented the following:

public function save($data,$type = 'body')
{
	if($this->id == null)
		return array( 'title'=>'','data'=>'');	
	
	if($type == $body)
	{
		$data['description'] = base64_decode($data['articletext']);
	}
	else
	{
		$data['title'] = strip_tags($data['title']); 
	}

	$this->table->save($data);
	
	//We need to process data as we are sending it back to the client
	
	$item = $this->table;
	$item->text = $item->description;
	
	//let's detect if any plugin tags are beig used 
	//if so let's inform the system to warn the user
	$message = $this->detectPluginTags($item->text);
		
	$params = new JObject;
	$params->set('inline',false); //set this so that we don't trigger the inline content plugins
	$dispatcher	= JEventDispatcher::getInstance();
	JPluginHelper::importPlugin('content');
	$dispatcher->trigger('onContentPrepare', array ('com_content.category', &$item, &$params, 0));
											
	return array( 'title'=>$item->title,'data'=>$item->text,'message'=>$message);	

An associated array of data will be passed into the save function from the post. So in our method, we check to see what type of data is being passed in, by checking the type flag.

If the type flag is set to ‘body’ then HTML data will be passed back to the save method; otherwise it will be text only.

 

The $data associative array will contain one of the following items:

 

Key

Value

articletext

HTML string or empty string

title

Text only string or empty string

 

If the data type is ‘body’, then the ‘articletext’ key will be set with the HTML data. If the type is set to ‘title’, then the title key will be set with the text only data, that was sent via post.

So, in case, for our MyCCK component for the category description, we set $data[‘description’] to $data[‘articletext]. We do this so that we can bind and save that data using our JTable instance.

Strictly speaking, you only need to do the following in this method if auto-save is enabled for inline editing. However, since this is the default behaviour we must make certain we do it.

Once the data is saved and completed, we have to get the updated data and render it back into the inline editor. This process is similar to the implementation of the triggerContentPlugins method.

The only real difference is this line:

$message = $this->detectPluginTags($item->text);

The detectPluginTags method we call, as the name suggests, checks for any plugin code in our content for our component. If this is the case, we generate a message to tell the user that because the content contains content plugin code, that it may not be rendered correctly, and so they may need to refresh the page.

 

Please note: In most cases, this will not likely happen; however, for those using JavaScript to further transform their content, this will most likely be the case.

 

In this function, we must return an associative array with the following structure:

 

Key

Value

title

Text only string or empty string

data

HTML or empty string

message

Text only string or empty string

 

Please note: that with the ‘title’ and ‘data’ values, at least one must be not an empty string.

So putting it all together, the code we needed to do for our category type, for our MyCCK component, is as follows:

class ARKContextsMyCCKCategory extends ARKContextsBase
{

	public function __construct($id)
	{
	    JTable::addIncludePath(JPATH_ADMINISTRATOR.'/components/com_mycck/tables');	       	
		$this->table = JTable::getInstance('category',’MyCCK’);
		$this->table->load($id);	
		return parent::__construct($id);	
	}	


    public function get()
	{
		if($this->id == null)
			return parent::get();	

		$this->table->articletext = $this->table->description;

		return parent::get();	
	}
	
	
	
	public function triggerContentPlugins($rawText)
	{
		
		$item = new stdclass;
					
		$item->text = '';
		if(isset($rawText))
		{
			$item->text = $rawText;
			$params = new JObject;
			$params->set('inline',false); //set this so that we don't trigger the inline content plugins
			$dispatcher	= JEventDispatcher::getInstance();
			JPluginHelper::importPlugin('content');
			$dispatcher->trigger('onContentPrepare', array ('com_mycck.category', &$item, &$params, 0));
			
		}	
			
		return array( 'data'=>$item->text);
	}
			
	public function save($data,$type = 'body')
	{
		if($this->id == null)
			return array( 'title'=>'','data'=>'');	
			
		if($type == $body)
		{
			$data['description'] = base64_decode($data['articletext']);
		}
		else
		{
			$data['title'] = strip_tags($data['title']);  //ensure only text is allowed
		}
	

		
		$this->table->save($data);
		
		//We need to process data as we are sending it back to the client
		
		$item = $this->table;
		$item->text = $item->description;
		
		//let's detect if any plugin tags are beig used 
		//if so let's inform the system to warn the user
		$message = $this->detectPluginTags($item->text);
			
		$params = new JObject;
		$params->set('inline',false); //set this so that we don't trigger the inline content plugins
		$dispatcher	= JEventDispatcher::getInstance();
		JPluginHelper::importPlugin('content');
		$dispatcher->trigger('onContentPrepare', array ('com_content.category', &$item, &$params, 0));
												
		return array( 'title'=>$item->title,'data'=>$item->text,'message'=>$message);	
	}
	
}

Read more: Chapter 3: Category type

  • Hits: 17

Chapter 3: Read and Update

Chapter 3: Read and Update

Next, we will deal with how we will read the data from the database, for our defined types, for inline editing, and how to update that data as well. 

You may be asking the question, have we already not got the data so why do we need to read it from the data again?

Well, the answer is Yes and No.

We do indeed have the data, but not necessarily in the format it needs to be stored in the database. In fact, the data could have been altered by Joomla’s content plugins, and possibly transformed by some JavaScript code upon rendering. 

Also we may not even have all the data, if we are in a category blog view, when we are trying to edit a blog item.

So taking all of the above into account, in summary, we have to read the data ourselves from the database, and retrieve the data, exactly, as it is stored in the database, without any data transformation, so that the end user can edit it. Once the data has been edited, we then need to send the edited data, back to Joomla to be saved.

It doesn’t finish there; we have to send back the saved data to the user and update the data, transform and render it as it was before the user edited it.

Read more: Chapter 3: Read and Update

  • Hits: 21

Chapter 3: Using Ajax

Chapter 3: Using Ajax

The basis of how we implement read and update operations when we edit our inline editable content, is that we use Ajax to do this, more specifically, we use Joomla’s com_ajax component.

We shall, now, go into detail on how to use our framework, to implement read and update operations for your component.

Okay, if you remember when we created our folder structure for our package, for inline editing support, we created three files: one for each editable type.

+files_inlinemycck   [FILES_INLINE + COMPONENT NAME]
|
------+contexts
	|
---+mycck            [COMPONENT NAME]
         |
	   -----category.php  	[TYPE NAME as explained in XML]
	   |
	   -----item.php        [TYPE NAME as explained in XML]
	   |
	   -----blog.php        [TYPE NAME as explained in XML]
|
index.html
|
inlinemyck.xml  [INLINE +  COMPONENT NAME].xml

As you can see, we have three inline editable types in our component:

  1. Category
  2. Item
  3. Blog

In each file, we will create a class that extends the ArkContextsBase. This is the class provided to us by the inline editing framework, that will allow us to do what we discussed above:

  1. Read the data from the database, for the specified inline type
  2. Save any updates made by the end user, on inline editing
  3. render/transform the data back to the user, after saving as it was before any inline editing

The abstract ArkContextsBase contains three methods that will allow our extended class to read, update and render the content for our inline types. 

abstract class ArkContextsBase
{
	public function get()

	public function triggerContentPlugins($rawText)
	
	public function save($data,$type = 'body'){}
}

Let us now see how this works for our simple MyCCK component, in the next section.

Read more: Chapter 3: Using Ajax

  • Hits: 16

Chapter 2: Approach 3 [Template override]

Chapter 2: Approach 3 [Template override]

This part is for those who want to add inline editing, using your template to add the inline editable regions. This is, in fact, similar to Approach 2. But you will use Joomla’s layout override system.

Okay before we start, we are going to create a directory structure as follows, if you have not already done so:

+files_inlinemycck   [FILES_INLINE + COMPONENT NAME]
|
------+contexts
|
---+mycck            [COMPONENT NAME]
|
-----category.php  	[TYPE NAME as explained in XML]
|
-----item.php        [TYPE NAME as explained in XML]
|
-----blog.php        [TYPE NAME as explained in XML]
|
index.html
|
inlinemyck.xml  [INLINE +  COMPONENT NAME].xml

Do not worry about the content you need to put into the files for now, that will be explained in detail later, for now just create the folders and files for your component.

Now, in your site’s template folder for your default template, you should make a copy of the existing view for your extension you want inline editing support.

So, the file system structure will be as follows:

TEMPLATE_NAME/html/EXTENSION_NAME/VIEW_NAME/LAYOUT_NAME.php

If you are using a template from a well know template club, there is a good chance they are probably doing this already.

So, for our MyCCK component, we will need to copy the item and category layout files and place them in the html folder as follows:

1.    TEMPLATE_NAME/html/com_mycck/item/default.php

2.    TEMPLATE_NAME/html/com_mycck/category/blog_item.php

3.    TEMPLATE_NAME/html/com_mycck/category/blog.php

Now, let's see how to adjust the layouts to add inline editing support.

Let us start with the default layout for the item view.

Please note: This is an oversimplified version of the actual layout file, so that we keep things simple and remove any unnecessary stuff that is only specific to our component, and may have no relevance for your component.

<?php
	$dispatcher = JEventDispatcher::getInstance();
	JPluginHelper::importPlugin('inline');
?>
<?php if ($params->get('show_title') || $params->get('show_author')) : ?>
<div class="page-header">
	<h2 class="name">
		<?php if ($params->get('show_title')) : ?>
			<?php 
				$title= $this->escape($this->item->title);
				$dispatcher->trigger('editable',array(&$title, array('id'=>$this->item->id,'context'=>'mycck','itemtype'=>'item','type'=>'title')));
				echo $title;	
			?>	
		<?php endif; ?>
	</h2>
</div>
<?php endif; ?>
<div class="item-body">
	<?php 
		$dispatcher->trigger('editable',array(&$this->item->text, array('id'=>$this->item->id,'context'=>'mycck','itemtype'=>'item')));
		echo $this->item->text; 
	?>	
</div>

So in our example, just as in Approach 2, we call the inline editing 'inline' plugin via Joomla’s event dispatcher class. And here, we fire off the plugin twice: Once to draw the editable region for the item title, and secondly for the item’s main body text.

The parameters passed into the inline plugin I will explain again, in case you skipped the previous section.

The plugin just requires two parameters.

•    The text to add editable reason

•    Configuration settings

 

Inline plugin configuration settings are as follows:

1.    Id of current item

2.    Component name

3.    Item Type (Item, Category, and  Blog)

4.    Type (Possible values are 'body' and 'title'. This defaults to body and can be omitted. The value ‘title’ means no HTML will be allowed, and any found in the content will be stripped out)

Now, let us look at what we do, to add inline editing support, to the category blog view:

<?php if ($params->get('show_title') || $this->item->publish == 0 || $this->item->params->get('show_author') ) : ?>
	<div class="page-header">

		<?php if ($this->item->params->get('show_title')) : ?>
			<h2 class="name">
				<?php if ($this->item->params->get('link_titles') && $this->item->params->get('access-view')) : ?>
					
					<a href="/<?php echo JRoute::_($this->item->link); ?>">
					<?php 
						$title= $this->escape($this->item->title);
						$dispatcher->trigger('editable',array(&$title, array('id'=>$this->item->id,'context'=>'mycck','itemtype'=>'item','type'=>'title')));
						echo $title;	
					?>	
					</a>
				<?php else : ?>
					<?php 
						$title= $this->escape($this->item->title);
						$dispatcher->trigger('editable',array(&$title, array('id'=>$this->item->id,'context'=>'mycck','itemtype'=>'item','type'=>'title')));
						echo $title;	
					?>	
				<?php endif; ?>
			</h2>
		<?php endif; ?>
	</div>
<?php endif; ?>
<?php 
	$dispatcher->trigger('editable',array(&$this->item->text, array('id'=>$this->item->id,'context'=>'mycck','itemtype'=>'item')));
	echo $this->item->text; 
?>

As you can see, this code implementation for the category is very similar to the item one. So, again we fire off the inline plugin, to add the editable region when we output the title and text properties from the blog item.

Please note: If you are going to override how the inline editing framework draws the editable region on the page, you will need to disable the ‘Auto draw editable regions in the components’ option, in the Ark Editor global configuration, under the inline tab, for the component you want to do this for.

Lastly, for completeness, we shall show you the code for the leading Category item in the blog view:

<div class="blog<?php echo $this->pageclass_sfx; ?>" itemscope itemtype="http://schema.org/Blog">
	<?php if ($this->params->get('show_category_title', 1) : ?>
		<h2>
			<?php if ($this->params->get('show_category_title')) : ?>
				<span class="category-title">
					<?php
						$title= $this->escape($this->item->title);
						$dispatcher->trigger('editable',array(&$title, array('id'=>$this->item->id,'context'=>'mycck','itemtype'=>'category','type'=>'title')));
						echo $title; 
					?>
				</span>
			<?php endif; ?>
		</h2>
	<?php endif; ?>
	<?php if ($this->params->get('show_description', 1) : ?>
	<div class="category-description">
		<?php if ($this->params->get('show_description') && $this->category->description) : ?>
			<?php 
					$text = JHtml::_('content.prepare', $this->category->description, '', 'com_content.category'); 
					$dispatcher->trigger('editable',array(&$text, array('id'=>$this->item->id,'context'=>'mycck','itemtype'=>'item')));
					echo $text;
			?>
		<?php endif; ?>
	</div>
.......

</div>

 

Read more: Chapter 2: Approach 3 [Template override]

  • Hits: 13