Subscribe: Posts / Comments / Email


RSS

Textpattern: My Flexible Plugin Attempt

Fri, May 22, 2009

Flex

My First plugin attempt, I wanted to make a power user editor for making quick edits to the textpattern database. You know that it gets old
waiting for the page to reload every time in textpattern when you just want to make a few quick changes to the sections. Ok lets see how far I got.

There is a plugin composer that you can download and it will help you out when pasting the code from
your IDE to textpattern, make sure you grab that.

Ok this is going to go fast. I also commented my code really good so make sure you check that out. Lets do this.

First you need to register the tab and the event that is going to be handled by the plugin.

Here is the first part of the plugin before the functions got put in.

$plugin [ 'name' ] = 'jps_db_manager';
$plugin [ 'version' ] = '0.1';
$plugin [ 'author' ] = 'Jonnie Spratley';
$plugin [ 'author_uri' ] = 'http://jonniespratley.com';
$plugin [ 'description' ] = 'Database Manager';
$plugin [ 'order' ] = '5';
$plugin [ 'type' ] = '3';//only on the admin side

if ( ! defined ( 'txpinterface' ) ) @include_once ( 'zem_tpl.php' );

# --- BEGIN PLUGIN CODE ---

if ( @txpinterface == 'admin' )
{
	$jpsDBTab = 'Fxk DB Manager';
	$jpsDBEvent = 'jps_db_manager';

	add_privs ( $jpsDBEvent, '1' );

/* ===============================================================================
* Register all callbacks and tabs, also permissions on all of the events
* That is something they don't tell you.
*
* I hate the resources on textpattern plugins, and the documentation, is shitty,
* actually... what documentation??
* ===============================================================================*/
register_tab ( 'extensions', $jpsDBEvent, $jpsDBTab );
register_callback ( $jpsDBEvent, $jpsDBEvent, null, 1 );

}//ends @txpinterface == admin

Now after that is in place, you have to create the callback function that will be triggered once the user clicks the tab we just registered. Before that we are going to have to create the mini private functions that help out the main function.

_getTables() – This gets all of the tables in the textpattern database. Then for every table in the
it creates a flex tree ready array holding the name of the table and the children which are the fields.


/**
 * I get all of the tables in the database
 *
 * @return [array] - A array ready for Flex
 */
function _getTables()
{
	//This is the textpattern config.php file. txpcfg holds the info in an assoc array;
	global $txpcfg;

	//Get the database name
	$dbDB = $txpcfg [ 'db' ];

	//Build the query
	$sql = 'SHOW TABLES FROM ' . $dbDB;

	//Get the results
	$query = getThings ( $sql );

	//Set up the result array
	$tables = array ();

	//Loop the results
	foreach ( $query as $key => $value )
	{
		//Set up a array for the fields
		$fields = array ();

		//For each table get the fields
		$fields = _describeTable ( $dbDB, $value );

		//and now each table, add the table name and the fields array
		$tables [] = array (
			'label' => $value, 'children' => $fields
		);
	}

	//Return that shit
	return $tables;
}

_describeTable( table ) – This gets all of the fields in the table.

/**
 * I describe a database table, I get information about the fields in the table.
 *
 * @param [string] $db - The database name from the txp configuration.
 * @param [string] $tbl - The table name that you want information about.
 * @return [array] An array containing the information.
 */
function _describeTable( $db, $tbl )
{
	$sql = 'SHOW FIELDS FROM ' . $db . '.' . $tbl;
	$results = getThings ( $sql );

	$fields = array ();
	//Loop the results from the getThings( sql ) method
	foreach ( $results as $key => $value )
	{
		//Add each field to the array
		$fields [] = array (
			'label' => $value
		);
	}

	//Return that shit
	return $fields;
}

jps_db_manager( event, step ) – This function will poop out the ui, which is going to be a heading,
and then a div tag containing my flex application. It also added the neccessary javascript to the page.
I have a switch statement inside of the body of the function that checks the url to see what to do,
but I am not sure how to implement that.

/**
 * array (
 * 'db' => 'fxktextpattern',
 * 'user' => 'root',
 * 'pass' => 'fred',
 * 'host' => 'localhost',
 * 'table_prefix' => '',
 * 'txpath' => '/Applications/MAMP/htdocs/fxk-textpattern/textpattern',
 * 'dbcharset' => 'utf8',
 * 'doc_root' => '/Applications/MAMP/htdocs',
 * )
 *
 *
 * @param [string] $event - what event does textpattern trigger,
 * ( categories, articles, forms, etc. ) Look at the end of the url and you will see it.
 * @param [string] $step - what event step is textpattern triggering,
 * ( for categories, when you create a category this is the step: category_article_create ).
 */
function jps_db_manager( $event, $step )
{
	//Default header and navigation, this took me so fucking long to figure out,
	//no other part of the textpattern admin was showing up.
	//and no one would ever tell you to watch out for that, fuck.
	pagetop ( "Textpattern", '' );//DONT FORGET TO HAVE THIS IF YOU WANT THE UGLY TEXTPATTERN TABS AND SHIT

	$dbJSON = '';//get the JSON ready

	//Start building the top of OUR plugin
	$html = '

Database Manager

'; //Shoot that shit out really quick. echo $html; /* ================================================================================== * First part is complete, now we are going to set up for our REST admin stuff * I know its tacky but so is textpattern, but I still love it <3; * * We are going to have a few different modes, and going to check if the url query * string has any of what we want, and if it does we want to go ahead and do the * correct function for it. * * Watch * ================================================================================== */ //the mode variable ( ie: http://textpattern/index.php?event=OUR_PLUGIN_EVENT_NAME&m=GETDATA ) $mode = '';// m = GETDATA $query = '';// q = the sql you want to call $dbJSON = json_encode ( _getTables () ); /** * Here is another shitty name for the textpattern variable, gps( what ); * this shit checks for a GET variable from the url or something. * So we check if the url has a m in it for our MODE */ if ( gps ( 'm' ) ) { //It does, good. set the m to the mode variable $mode = gps ( 'm' ); //Good there is a mode so lets check for a q if( gps ( 'q' ) ) { $query = gps ( 'q' ); } //Now lets switch depending on what mode is specified switch ( $mode ) { //case they want the tables, call the getTables() and set the return value to the $dbJSON variable we declared earlier case 'getTables': $dbJSON = json_encode ( _getTables () ); break; case 'execute': $dbJSON = json_encode( _executeQuery( $query ) ); break; } } //ends mode check $html = <<

Content on this page requires a newer version of Adobe Flash Player.

Get Adobe Flash player
EOF; echo $html; }

Now that the plugin code is finished, it was time to goto the flex side. Notice that the javascript functions inside of the code, that is what I am going to be calling on, via flex’s external interface.

Here is the actionscript that will be calling on the javascript that I placed inside of the page.

import com.adobe.serialization.json.JSON;
import mx.collections.ArrayCollection;
import mx.events.ListEvent;

[Bindable] private var isResults:Boolean = false;
[Bindable] private var isAvailable:Boolean = false;
[Bindable] private var isProcessing:Boolean = false;
[Bindable] private var isValid:Boolean = false;
[Bindable] private var resultCollection:ArrayCollection = new ArrayCollection();
[Bindable] private var tableCollection:ArrayCollection = new ArrayCollection();

private function init():void
{
	//Register all event listeners
	btn_clear.addEventListener( MouseEvent.CLICK, clear );
	btn_execute.addEventListener( MouseEvent.CLICK, execute );
	btn_refresh.addEventListener( MouseEvent.CLICK, refresh );
	tree_tables.addEventListener( ListEvent.CHANGE, onTreeChange );
	isAvailable = ExternalInterface.available
	getTables();
	registerCallbacks();
}

private function registerCallbacks():void
{
	ExternalInterface.addCallback( 'gotTables', onGotTables );
}

private function onGotTables( raw:String ):void
{
	var jsonArray:Array = ( JSON.decode( raw ) as Array );
	tableCollection = new ArrayCollection( jsonArray );
}

private function executeQuery( query:String ):void
{
	var raw:String = ExternalInterface.call( 'execute', query );
	var jsonArray:Array = ( JSON.decode( raw ) as Array );

	resultCollection = new ArrayCollection( jsonArray );
}

private function getTables():void
{
	var raw:String = ExternalInterface.call( 'getTables' );
	var jsonArray:Array = ( JSON.decode( raw ) as Array );

	tableCollection = new ArrayCollection( jsonArray );
}

private function refresh( event:MouseEvent ):void
{
	getTables();
}

private function clear( event:MouseEvent ):void
{
	txt_query.text = '';
}

private function execute( event:MouseEvent ):void
{
	//Todo
}

private function onTreeChange( event:Event ):void
{
	//Todo
	ExternalInterface.call( 'tableSelected', event.currentTarget.selectedItem.label );
}

And the view





	
	
		
	




	
		
		
	
	
		
		
		
	

This is where I got stuck, I don’t know how to go about when a user selects a table, in flex, then send that table to javascript, then javascript to php?

Tags: , ,

1 Comments For This Post

  1. Carl Shoaf Says:

    Great blog, this site has really been an eye opener. I need some time to think about this

    Follow me on Twitter

Leave a Reply