Please login or register.

Login with username, password and session length
Advanced search  

Author Topic: [Tutorial] Custom Template Functions  (Read 5445 times)

nukpana

  • Hero Member
  • *****
  • Karma: 71
  • Posts: 663
[Tutorial] Custom Template Functions
« on: March 08, 2010, 02:18:49 AM »

In this iteration of the tutorial series, I will cover better template options that can help with presenting your web site using the sNews CMS.

One may ask, what do I mean by that? 
Ok, some, including myself, have noted a more hands off approach or easier way to do the front end development of your sNews site.  In my opinion, the system should only serve the content without styling it, so the front end developers don't have to edit the core and potentially break the system or future upgrades.

Ok, how do we go about doing that?
Well we really don't have to hack the core up some and this can be the basis for the next iteration of the sNews core for the front end. Let's take a look.

The idea here is to bring back control of styleing to the user for thier templates.  Let's start by making a template function file and loading it with some wrapper functions and other functions. 

Let's back up our index.php file, then load up the template functions file like so (you can calls your whatever you want, I have mine called tpl_func.php).
Code: [Select]
<?php include('snews.php'); include('tpl_func.php'); ?>
Now, let's take a look at the first call from sNews:
Code: [Select]
<?php title(); ?>
If we didn't know better, we could guess that it just gives us the title tags with content. Our luck, it gives more, and unless we hack at it, we can't do much about it.  Unless we don't use it:
Code: [Select]
<head>
<base href="<?php echo _SITE?>" />
<title><?php echo header_title(); ?></title>
<meta http-equiv="Content-Type" content="text/html; charset=<?php echo curr_charset(); ?>" />
<meta name="description" content="<?php echo description(); ?>" />
<meta name="keywords" content="<?php echo keywords(); ?>" />
<meta name="robots" content="index,follow" />
<meta name="author" content="Solucija.com" />
<?php admin_js(); ?>
<link rel="stylesheet" type="text/css" href="css/style.css" />
</head>

For the title function, the only two items sNews requires is the Base Href and the admin_js, everything else is optional.  So leave it up to me to decide if i want it or not. Ready?

Code: [Select]
function header_title() {
global $_TITLE, $_NAME, $_XNAME;
    $title =  $_TITLE ? $_TITLE.' - ' : '';
    $title .= $_NAME ? $_NAME.' - ' : '';
    $title .= $_XNAME ? $_XNAME.' - ' : '';
    if (
check_category($categorySEF) == true
&& $categorySEF != 'administration'
&& $categorySEF
) {
    $title .= l($categorySEF).' - ';
}
$title .= s('website_title');
return $title;
}

// Subjective and may not have needed this...
function curr_charset() {
return s('charset');
}

function description() {
global $categorySEF, $_DESCR;
$desc = !$categorySEF
? s('website_description')
: $_DESCR;
return $desc;
}

function keywords() {
global $categorySEF, $_KEYW;
$keys = !$categorySEF
? s('website_keywords')
: $_KEYW;
return $keys;
}

function admin_js() {
if (_ADMIN) {
echo '<script type="text/javascript">';
include('js/admin.js');
echo '</script>';
}
}

Right off the bat, we broke up function title.  header_title uses the same formula title() uses.  curr_charset is debatable whether or not to put it in a wrapper function, but putting Description and Keywords shouldn't be. Now, I can place them wherever I want on the page, even in the front end, since the are wrapped in a function.

A big benifit is that, I the user/template writer, control how, and where the site is presented, and it doesn't break the core.

Want another example?? Ok


The next template call is pages().  Nothing really wrong with the function, just uses code to get HTML code, which I would rather do and not the system. 

Note, if you are using my extra fix, it benifits you as you can utilize the base pages as normal pages - hide, show, reorder etc.

Code: [Select]
define( '_VIS', (_ADMIN ? ' AND visible=\'YES\'' : '' ) );

// Requires http://snewscms.com/forum/index.php?topic=7996.0
function page_list() {
$pageResult = array();
$pageResult[] = array( "seftitle" => "", "title" => l('home') );
$query = 'SELECT id, seftitle, title
FROM '._PRE.'articles
WHERE position = 3
'._VIS.'
ORDER BY artorder ASC, id';
$result = mysql_query($query);
while ( $r = mysql_fetch_assoc( $result ) ) {
if ( $r['id'] != s( 'display_page' ) ) {
unset( $r['id'] );
$pageResult[] = $r;
}
}
return $pageResult;
}

The code above is what is placed in the template function file.  What is it doing you ask?? We are getting our data and putting it in an array to be outputted.  We return an array that let's the template parse it.  Our data is modelled and ready to be sent to the template/view as such:

Replace:
Code: [Select]
<?php pages(); ?>
With:
Code: [Select]
<?php 
foreach ( 
page_list() as $page ) {
echo '<li><a href="'.$page['seftitle'].'">'.$page['title'].'</a></li>';
}
?>

If you know the basics of PHP, this should be an easy one as we are calling a foreach loop to go through each part of the array and echoing it. Pretty similar to a Database loop. 

If you don't know foreach read up on it here:
http://us3.php.net/manual/en/control-structures.foreach.php

What about functions with errors?
Try the RSS Links function:

Code: [Select]
function rssfeed_links() {
$rsslink = $l_error = array();
$query = '
SELECT COUNT(id) as articles_count,
(SELECT COUNT(id) FROM '._PRE.'articles
WHERE position = 3 AND published = 1) as pages_count,
(SELECT COUNT(id) FROM '._PRE.'comments
WHERE approved = "True" ) as comments_count
FROM '._PRE.'articles
WHERE position = 1 AND published = 1';
$result = mysql_query( $query );
while ($r = mysql_fetch_assoc($result )) {
foreach ($r as $k => $v) {
if ( $v > 0 ) {
$l = explode('_', $k);
$rsslink[] = array('seftitle' => 'rss-'.$l[0], 'title' => l( 'rss_'.$l[0] ) );
} else {
$l_error[] = $k;
}
}
}
if (count($l_error) == 3) {
$rsslink['none'] = l('no_rss');
}
return $rsslink;
}

In our template:
Replace:
Code: [Select]
<?php rss_links(); ?>
With:
Code: [Select]
<?php 
$rsslinks rssfeed_links();
if (isset($rss['none'])) {
echo '<li>'.$rss['none'].'</li>';
} else {
foreach( rssfeed_links() as $rss ) {
echo '<li><a href="'.$rss['seftitle'].'">'.$rss['title'].'</a></li>';
}
}
?>


If you notice, all we are working with are simple structured array's and foreach loops.  Nothing advanced or ground shattering, and it is stuff you can do right now.

Here I have shown a few ways to get better and more control of your templating functions. By allowing the system to only server the bare content and allowing you to apply the HTML you want, your sNews site is more and more customized and unqiue and you don't need to break or hack the core to do it!
Logged

Patric Ahlqvist

  • Nobodys perfect, but Im pretty effing close
  • ULTIMATE member
  • ******
  • Karma: 65
  • Posts: 4867
  • “I'm a self-made man and worships my creator.”
    • p-ahlqvist.com
Re: [Tutorial] Custom Template Functions
« Reply #1 on: March 08, 2010, 02:36:34 PM »

Mhm, Have no coding skills whatsoev'a, but the general idea appeals to me for sure... The more I, as frontend designer, can decide the better, I think
Logged
"It's only dead fish that goes with the flow... "
Updated

Sven

  • ULTIMATE member
  • ******
  • Karma: 88
  • Posts: 2029
  • Chasing MY bugs!
    • hiseo.fr - rédacteur Web
Re: [Tutorial] Custom Template Functions
« Reply #2 on: March 08, 2010, 03:57:26 PM »

Hello.
If you know the basics of PHP, this should be an easy one
Goodbye.
 ;D

forsooth

  • Newbie
  • *
  • Karma: 1
  • Posts: 23
Re: [Tutorial] Custom Template Functions
« Reply #3 on: March 08, 2010, 05:26:53 PM »

Thank you for posting this.  I have been going through it this morning, and I can follow most of your code.  It is a good learning tool for someone like me.  The commentary between code blocks is very helpful. 

(Could have used more comments -- haha...Never satisfied!  ;))
Logged

nukpana

  • Hero Member
  • *****
  • Karma: 71
  • Posts: 663
Re: [Tutorial] Custom Template Functions
« Reply #4 on: March 10, 2010, 05:59:23 AM »

Thanks Everyone.  I added some (untested) code here: http://snewscms.com/forum/index.php?topic=8172.msg63531#msg63531 to help out in anyway with writing code.
« Last Edit: March 11, 2010, 03:44:28 AM by nukpana »
Logged

nukpana

  • Hero Member
  • *****
  • Karma: 71
  • Posts: 663
Re: [Tutorial] Custom Template Functions
« Reply #5 on: July 08, 2010, 04:33:15 PM »

Part 2a:  Custom Template Functions.

I am going to show some customization functions that will help build the current 1.7 template.  

If you have been following this *series* you will note that I have been using some logic and some includes to help separate the template pages, now I will break this down further here.

Please note, this is a Proof of Concept only, not live code that should be used.  I will admit, I didn't check for bugs, security or what not. If it worked for my in my testing, then I am using it here. I expect you to respect that and test further if you plan to go live with this code.

** Note - there is a class (OOP) and I am planning a few more. They will be easy to understand and use.  I promise! **

0. Use a fresh install or backup.  I prefer to use a new test install.  We will not be using the internal pages: Archive, Sitemap and Contact, you will see why in a second.

1. Backup index.php and add the following code:
Code: [Select]
<?php


require(
'snews.php');

// define the template we want to use - this is a basic form of template switching

$template 's17';
// The tpl folder can be named differently if you prefer

include('tpl/' $template .'/index.php');


?>

So far the only modification to the core system is this.

2. Prepare a new folder/file structure under the root sNews install - the slashes note a folder:
Code: [Select]
/snews17
/tpl
tpl.core.php
/s17
index.php
/admin
/css
/inc
/js
index.php
/public
/css
/inc
/js
index.php

2a Lets go over what is going on here. As note previously I am building a simple template switcher, but within the template, I am breaking it down further to public and admin sections.
 - tpl folder  - is the folder that will house all the templates and their relevant files
 - tpl.core.php - this is the file that I assume would contain core functionality
 - s17/index.php - this is the index.php file, but it will serve as a controller to the public and admin sections (thier index.php files respectively)
Each section under the main index (s17/index.php) will have an index.php and 3 folders for presentation(css), behaviour(js), and any additional serverside scripting(inc)
 
3. Let's starting building the tpl.core.php file.  I will add commentary in the file that can be removed (noted as "EQ Note - ").  Some you will have seen previously in other threads....
Code: [Select]
<?php

// EQ Note - this should be a gimmie. The visibility query shows up in alot of queries in the core system  
// Content visibility - noted in SQL Queries
define'_VIS', (_ADMIN ' AND visible = \'YES\'' '' ) );


// EQ Note - ok, so you wondered why no Archive, Sitemap and Contact.  It if isn't obvious, this is noting the admin pages - IMO, the only pages the system to generate.  Additionally, ...well it shouldn't be here and it should be up to the user/dev to decide if they want this - hence this tutorial. This would in a sense replace the cat_listSEF variable.
// EQ Note 2 - You will note the style of the array_push. It is modeled after Rudy Limeback's style of SQL writing. Quiz question would be why???? LOL  It being more readable and cleaner is one answer.
// Get list of system pages - w/o Archives, Sitemap, Contact
function sys_pages() {
$arr = array();
$arr['seftitle'][] = 'login';
if ( _ADMIN ) {
array_push$arr['seftitle'],  
  'administration'
'admin_category'
'admin_article'
'article_new'
'extra_new'
'page_new'
'snews_categories'
'snews_articles'
'extra_contents'
'snews_pages'
'snews_settings' 
'snews_files'
'logout'
'groupings'
'admin_groupings'
);
// EQ Note - I wanted to capture all admin sef and action uris to check against
$arr['action'] = array(
  'process'
'editcomment'
);
}
return $arr;
}

/* UNTESTED 

If you want archives, contact and sitemap out, but want to use the SEF here is untested hack for use with this Proof of Concept

// Such a f'n hack!
$sys_pages = sys_pages();
if ($url[0] && !$_ID) {
if(in_array(
$url[0], array_diff(
  explode(',', l('cat_listSEF'))
, $sys_pages['seftitle']
)
)
) {
if (retrieve('id', 'articles', 'seftitle', $url[0])) {
$categorySEF = $url[0];
$_POS = 3;
$query = 'SELECT id, title FROM '._PRE.'articles WHERE seftitle = "'.$url[0].'"';
$result = mysql_query($query);
while($r = mysql_fetch_assoc($result)) {
$_ID = $r['id'];
$_TITLE = $r['title'];
}
unset($r, $result, $query);
} else {
$categorySEF = '404';

header('HTTP/1.1 404 Not Found');
}
}
}
unset($sys_pages);
*/

// EQ Note - get_uri($var) is similar to the old get_id or the SEF globals categorySEF, articleSEF, etc, with the exception of this fact: This will filter and bring back what the item SHOULD BE, not what the variable is called. For example the global $categorySEF may be a category or may be a page. SubcatSEF may be a subcategory or an article. Now I can safely call this function and know what I am calling is what I should be getting - checked against the system.
/* Current parts of the $var
paginSEF - the pagination SEF ie p_2
c_paginSEF - the comment pagainstion SEF ie c_2
categorySEF
articleSEF
pageSEF
404
home
admin_action
*/
// Gets list of current url as it's defined
function get_uri$var ) {
static $arr;
if (!$arr) {
global $url$categorySEF$_catID$_XNAME$_ID$_POS;
$arr = array();
$sysPage sys_pages();


if ( $url ) {

// Pagination SEF - return page number
if ( array_search_beginningl('paginator'), $url ) !== FALSE ) {

$paginSEF array_search_beginningl('paginator'), $url );
if ( is_numeric(substr($url[$paginSEF], 21)) ) {
$arr['paginSEF'] = $url[$paginSEF];
}

}

// Comments Pagination SEF - return comments page number
if ( array_search_beginningl('comment_pages'), $url ) !== FALSE ) {

$c_paginSEF array_search_beginningl('comment_pages'), $url );
if ( is_numeric(substr($url[$c_paginSEF], 21)) ) {
$arr['c_paginSEF'] = $url[$c_paginSEF];
}

}

// AdminSEF
if ( $url[0] && in_array$url[0], $sysPage['seftitle'] ) ) {
$arr['adminSEF'] = $url[0];
}

// PageSEF
if( $url[0] && $_ID && $_POS == ) {
$arr['pageSEF'] $url[0];
}

// CategorySEF
if( $url[0] && $_catID ) {
$arr['categorySEF'] = $url[0];
}

// SubcatSEF
if( $url[1] && $_catID && $_XNAME ) {
$arr['subcatSEF'] = $url[1];
}

// ArticleSEF
if ( $url[2] && $_ID && $_POS == ) {
$arr['articleSEF'] = $url[2];
} elseif ( $url[1] && $_ID && $_POS == ) {
$arr['articleSEF'] = $url[1];
}

// 404 - as defined by the main sNews query - EQ NOTE - I don't have a fix, but I don't have archives, contact, or sitemap. If you link it, it will not return a 404 as it is defined in the main file ******* Note, a few min later I hacked a fix.. see above... ***********
if ($categorySEF == '404') {
$arr['404'] = '404';
}
}

// Query Strings
if ( _ADMIN ) {

// Action
if ( isset($_GET['action']) 
&& ( in_array$_GET['action'], $sysPage['seftitle'] )
  || in_array$_GET['action'], $sysPage['action'] ) 
  )
) {
$arr['admin_action'] = clean(
cleanXSS($_GET['action'])
);
}

}

// Home Page
if ( empty($arr) ) {
$arr['home'] = 'home';
}
}
return $arr[$var];
}

// EQ Note - I noted previously some helper functions, and to help I am including them here with some updates. It sortof saves the touble of calling some of the usual globals in the functions
// From http://snewscms.com/forum/index.php?topic=8172.msg63531#msg63531

// Global wrappers
function content_id() {
global $_ID;
return $_ID;
}

function content_title() {
global $_TITLE;
return $_TITLE;
}

function position() {
global $_POS;
return $_POS;
}

function cat_id() {
global $_catID;
return $_catID;
}

function cat_name() {
global $_NAME;
return $_NAME;
}

function parent_name() {
global $_XNAME;
return $_XNAME;
}

// EQ NOTE - does the admin need a description? I don't think so... 
function description() {
global $_DESCR;
if (is_admin_page()) return;
$desc is_home()
s('website_description'
$_DESCR
return $desc;
}

function keywords() {
global $_KEYW;
if (is_admin_page()) return;
$keys is_home() 
s('website_keywords'
$_KEYW;
return $keys;
}

// EQ Note - These 2 are for checks for new items....
// Is true??
function is_lang_var$var ) {
if ( l($var) ) {
return TRUE;
}
}

function is_setting$name ) {
if ( s($name) ) {
return TRUE;
}
}

// EQ Note - the next is_ functions are good to have!!!
function is_admin_page() {
$is_admin FALSE

$sysPage sys_pages();
if ( in_arrayget_uri('adminSEF'), $sysPage['seftitle'] ) ) {
$is_admin TRUE;
}
if ( _ADMIN 
&& ( in_arrayget_uri('admin_action'), $sysPage['seftitle'] ) 
||  in_arrayget_uri('admin_action'), $sysPage['action'] ) 
)
) {
$is_admin TRUE;
}
return $is_admin;
}

function is_home() {
if ( get_uri('home') ) {
return TRUE;
}
}
function is_article() {
if ( position() == ) {
return TRUE;
}
}
function is_page() {
if ( position() == ) {
return TRUE;
}
}
function is_category() {
if (   cat_id()
 && !position() 
 &&  !is_admin_page() 
) {
return TRUE;
}
}

/***** Other Helpers *****/

function curr_url() {
return 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
}

// EQ NOTE - this is used for pagination links....
// http://www.php.net/manual/en/function.array-search.php#70001
function array_search_beginning$str$arr ) {
    
if ( is_array($arr) ) { 
        
$strlen strlen($str); 
        
foreach ( $arr as $k => $v ) {
            
$part substr($v0$strlen);
            
if ($str == $part) { 
                
return $k
}
}
}
return FALSE;
}

// Based on http://herdian.ferdianto.com/2009/05/21/simple-routing-library
// Ex: dispatch('!^$!', 'user_function'); calls for home
// Ex: dispatch('!^contact$!', 'user_function'); calls for contact page
// Ex: dispatch( '!^uncategorized(.*)$!', 'user_function' ); calls for category and subsequent subcats/articles
function dispatch$pattern$callback ) {
$path str_replace(
dirname($_SERVER['PHP_SELF']), 
''
$_SERVER['REQUEST_URI']
);
$path ltrim($path'/');
if (preg_match($pattern$path$match)) {
if (function_exists($callback)) {
return call_user_func($callback$match);
}
}
}

function format_date($var) {
return date(s('date_format'), strtotime($var));
}

// EQ NOTE - this may be puzzling as to why this is here, but it is a good example of separation of Model and View, core and template.  If the user/dev, wanted to use the matchCaptcha, then they would have to use the HTML provided in the core.  This takes away the HTML and just gives what is needed - Number 1, 2 and the required name call in the input.
// Numeric captcha
function captcha() {
$arr = array();
$x rand(19);
$y rand(19);
$_SESSION[_SITE.'mathCaptcha-digit'] = $x $y;

$arr['num1'] = $x;
$arr['num2'] = $y;
$arr['req'] = 'name="calc"';
return $arr;
}

function admin_js() {
if (_ADMIN) {
echo '<script type="text/javascript">';
include('js/admin.js');
echo '</script>';
}
}

/*** Content Specific ***/

// Single page or full article specific functions
//  EQ Note - I wanted this in the content class I have, but file_include echo's the content instead of returning it, so the burden is left to the template writer
function parse_text$text ) {
if ( content_id() ) {
$txt str_replace('[break]''',$text);
} else {
$text explode'[break]'$text );
$txt $text[0]; 
}
return file_include$txt9999000 );
}

// Admin Links

// EQ note - next 4 are self explanitory, no?  Note each function will return an array of link and title. 

// Build the edit link
function edit_link$id ) {
if (!_ADMIN) return;
$qs = array(
'action' => 'admin_article',
'id' => $id
);
$qs http_build_query$qs'''&amp;' );
return array( 
'link' => _SITE.'?'$qs
'title' => l('edit'
);
}

// Build the show/hide admin link
function visible_link$id$vis$category ) {
if (!_ADMIN) return;
$vis $vis == 'YES' 
'hide' 
'show';
$qs = array(
'action' => 'process',
'task' => $vis,
'item' => 'snews_articles',
'id' => $id,
'back' => $category
);
$qs http_build_query$qs'''&amp;' );
return array( 
'link' => _SITE.'?'$qs
'title' => l$vis 
);
}

function edit_cmnt_link$id ) {
if (!_ADMIN) return;
$qs = array(
'action' => 'editcomment',
'commentid' => $id
);
$qs http_build_query$qs'''&amp;' );
return array( 
'link' => _SITE.'?'$qs
'title' => l('edit'
);
}

function del_cmnt_link$id ) {
if (!_ADMIN) return;
$qs = array(
'action' => 'process',
'task' => 'deletecomment',
'commentid' => $id
);
$qs http_build_query$qs'''&amp;' );
return array( 
'link' => _SITE.'?'$qs
'title' => l('delete'
);
}

// EQ Note - Ok, this feels hacky to me somehow... I would like to rewrite this, but for now...
// Current Category for show/hide back link
function currCategory() {
global $articleSEF$categorySEF$subcatSEF$_POS;
switch( TRUE ) {
// should group these 2 cases....
case ( $_POS == && $articleSEF ):
$currCat $categorySEF.'/'.$subcatSEF;
break;
case ( !$_POS && $subcatSEF ):
$currCat $categorySEF.'/'.$subcatSEF;
break;
case ( $categorySEF ):
$currCat $categorySEF;
break;
case ( empty( $categorySEF ) ):
$currCat '';
break;
}
return $currCat;
}

// EQ Note - these 2 functions just strips the language from the sef.  ie p_2 is returned as 2
// Pagination Functions
function page_num() {
return str_replacel('paginator'), ''get_uri('paginSEF') );
}

function cmnt_page_num() {
return str_replacel('comment_pages'), ''get_uri('c_paginSEF') );
}

// EQ Note - the next 2 should be self explanitory
function maxArticleNum() {
static $maxArticles;

if (!$maxArticles ) {
// Constant is not used because we need the table alias
$visible ADMIN '' ' AND a.visible=\'YES\' ';

if ( cat_id() ) {
$count 'SELECT 
COUNT(a.id) AS num
FROM  '
._PRE.'articles AS a
WHERE position = 1
AND a.published =1
AND category = '
.cat_id().$visible.'
GROUP BY category'
;
} else {
$count 'SELECT 
COUNT(a.id) AS num
FROM '
._PRE.'articles AS a
LEFT OUTER JOIN '
._PRE.'categories as c
ON category = c.id
LEFT OUTER JOIN '
._PRE.'categories as x
ON c.subcat =  x.id 
AND (x.published =\'YES\')
WHERE show_on_home = \'YES\' '
.$visible.'
AND position = 1
AND a.published =1
AND c.published =\'YES\'
GROUP BY show_on_home'
;
}
$count mysql_query($count);
if ($count) {
$r mysql_fetch_array($count);
$maxArticles $r['num'];
}
}
return $maxArticles;
}

function maxCommentNum() {
static $maxComments;

if (!$maxComments ) {
$count 'SELECT 
COUNT(id) as num
FROM '
._PRE.'comments
WHERE articleid = '
.content_id().'
AND approved = \'True\''
;
$count mysql_query($count);
if ($count) {
$r mysql_fetch_array($count);
$maxComments $r['num'];
}
}
return $maxComments;
}


/* Example:
Category Articles:
paginationLinks( 
page_num(), 
s('article_limit'), 
maxArticleNum() 
);
Comments:
paginationLinks( 
cmnt_page_num(), 
s('comment_limit'), 
maxCommentNum() 
);
*/

function paginationLinks$currNum$limit$maxNum ) {
$totalPages ceil$maxNum $limit );
if ($totalPages 1) {

if ( !$currNum ) {
$prevPage '';
$nextPage 2;
} elseif ( $currNum != $totalPages ) {
$prevPage $currNum 1;
$nextPage $currNum 1;
} elseif ( $currNum == $totalPages ) {
$prevPage $currNum 1;
$nextPage '';
}

return array(
  'previous-page' => $prevPage
'next-page'    => $nextPage
'last-page'    => $totalPages
);
}
}

function comment_list() {
$arr = array();

if ( maxCommentNum() > ) {
$offset cmnt_page_num() > 
? (cmnt_page_num() - 1) * s('comment_limit')
0;
$ordinal 1;
$query 'SELECT *
FROM '
._PRE.'comments
WHERE articleid = '
.content_id().'
AND approved = \'True\'
ORDER BY id '
.s('comments_order').'
LIMIT '
.$offset.', '.s('comment_limit');
$result mysql_query($query);
while($r mysql_fetch_assoc($result)) {
$r['cmnt_num'] = $offset $ordinal;
$arr[] = $r;
}
$ordinal++;
}
return $arr;
}

// EQ Note - I left a few functiosn out because I am still experimenting with them in my free time

?>


4. In s17/index.php add the following code:
Code: [Select]
<?php 

// s17 Template
require( 'tpl/tpl.core.php' ); 

// Controller
switch (true) {
case ( is_admin_page() ) :
include('admin/index.php');
break;

case ( !is_admin_page() ) :
include('public/index.php');
break;
}

?>

5. Likewise in s17/public/index.php add the following code and create the blank relevant files under the public folder:
Code: [Select]
<?php

require( 
'inc/functions.php' ); 
include('header.tpl.php');
// Controller
switch (true) {

case ( is_home() || get_uri('paginSEF') == $url[0] ) :
include('home.tpl.php');
break;

case ( is_category() ) : 
include('category.tpl.php');
break;

case ( is_article() || is_page() ) :
include('single.tpl.php');
break;

}
include('sidebar.tpl.php');
include('footer.tpl.php');

?>


Rest - be back later to add more!!
« Last Edit: July 08, 2010, 05:59:26 PM by nukpana »
Logged

nukpana

  • Hero Member
  • *****
  • Karma: 71
  • Posts: 663
Re: [Tutorial] Custom Template Functions
« Reply #6 on: July 08, 2010, 09:51:51 PM »

Picking up where I last left.  I am continuing to create a usual PHP introduction, header/footer/sidebar template for inclusion for the template as shown in Step 5.

6. If you have created the file s17/public/inc/functions.php then let's start adding to it to create our header.tpl.php
Code: [Select]
<?php

// EQ Note - The next 2 functions are about the same as the first post
function header_title() {
global $categorySEF$_TITLE$_NAME$_XNAME;
   
$title =  $_TITLE $_TITLE.' - ' '';
   
$title .= $_NAME $_NAME.' - ' '';
   
$title .= $_XNAME $_XNAME.' - ' '';
   
if (
check_category($categorySEF) == true 
&& $categorySEF != 'administration' 
&& $categorySEF
) {
   
$title .= l($categorySEF).' - ';
}
$title .= s('website_title');
return $title;
}

// Pages
function page_list() {
$pageList = array();
$pageList[] = array( 
  "seftitle" => ''
"title" => l('home'
);
$query 'SELECT 
  id
, seftitle
, title 
FROM '
._PRE.'articles
WHERE position = 3 
'
._VIS.
ORDER BY artorder ASC, id'
;
$result mysql_query($query);
while ( $r mysql_fetch_assoc($result) ) { 
if ( $r['id'] != s('display_page') ) {
unset($r['id']);
$pageList[] = $r
}
}
return $pageList;
}

// EQ Note - Not too happy about this function....
//BREADCRUMBS
function crumb_list() {
global $_NAME$_XNAME$_TITLE;
$arr = array();

// Prepare filtered URI array
$URL $_GET['category'];
$URL str_replace(get_uri('paginSEF'), ''$URL);
$URL str_replace(get_uri('c_paginSEF'), ''$URL);
$URL explode('/'$URL);
$URL array_filter($URL);

// Admin
if (_ADMIN) {
$arr[] = array( 
'link'=>_SITE.'administration/'
'title'=>l('administration'
);
}

// Home
$home = !$URL
'' 
_SITE;
$arr[] = array( 
'link'=>$home
'title'=>l('home'
);

if ( $URL[0] ) {
// domain.com/page
if ( get_uri('pageSEF') ) {
$arr[] = array( 
'link'  => ''
'title' => $_TITLE 
);
// domain.com/category/subcategory
} elseif ( get_uri('categorySEF') && get_uri('subcatSEF')) {
$arr[] = array( 
'link'  => _SITE.get_uri('categorySEF').'/'
'title' => $_XNAME 
);
// domain.com/category/article
} elseif ( get_uri('categorySEF') && get_uri('articleSEF') ) {
$arr[] = array( 
'link'  => _SITE.get_uri('categorySEF').'/'
'title' => $_NAME 
);
// domain.com/category
} else {
$arr[] = array( 
'link'  => ''
'title' => $_NAME  
);
}
}

if ($URL[1]) {
// domain.com/category/subcategory/article
if ( get_uri('subcatSEF') && get_uri('articleSEF') ) {
$arr[] = array( 
'link'  => _SITE.get_uri('categorySEF').'/'.get_uri('subcatSEF').'/'
'title' => $_NAME 
);
// domain.com/category/subcategory
} elseif ( get_uri('subcatSEF') && !get_uri('articleSEF') ) {
$arr[] = array( 
'link'  => '',
'title' => $_NAME 
);
// domain.com/category/article
} elseif ( !get_uri('subcatSEF') && get_uri('articleSEF') ) {
$arr[] = array( 
'link'  => '',
'title' => $_TITLE 
);
}
}

if ($URL[2]) {
// domain.com/category/subcategory/article
if ( get_uri('articleSEF') ) {
$arr[] = array( 
'link'  => '',
'title' => $_TITLE 
);
}
}

return $arr;
}


?>


7. Now let's make our s17/public/header.tpl.php file.  This is the first part of the actual template we are making!  I am having this as HTML5...  You will also note, there is no rocket science applied here as noted previously.
Code: [Select]
<!DOCTYPE html>
<html lang="<?php echo strtolower(s('language')); ?>">
<head>
<base href="<?php echo _SITE?>" />
<title><?php echo header_title(); ?></title>

<meta http-equiv="Content-Type" content="text/html; charset=<?php echo s('charset'); ?>" />
<meta name="keywords" content="<?php echo keywords(); ?>" />
<meta name="description" content="<?php echo description(); ?>" />

<link rel="stylesheet" type="text/css" media="all" href="tpl/s17/public/css/style.css" />

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<?php admin_js(); ?>
</head>
<body>
<div id="wrapper">
<div id="header">

<div id="top">
<ul id="topmenu">
<?php foreach(page_list() as $page): ?>
<li><a href="<?php echo _SITE.$page['seftitle']; ?>"><?php echo $page['title']; ?></a></li>
<?php endforeach; ?>
</ul>
<div id="search">
<form id="search_engine" method="post" action="<?php echo _SITE?>" accept-charset="<?php echo s('charset');?>">
<p>
<input class="searchfield" name="search_query" type="text" id="keywords" value="<?php echo l('search_keywords'); ?>" />
<input class="searchbutton" name="submit" type="submit" value="<?php echo l('search_button')?>" />
</p>
</form>
</div>
</div>

<div id="logo">
<h1><?php echo s('website_title'); ?></h1>
<p><?php echo s('website_description'); ?></p>
</div>

</div><!-- End Header Div -->
<div id="crumbs">
<?php foreach(crumb_list() as $crumb) {
if ( empty($crumb['link']) ) {
echo $crumb['title'];
} else {
echo '<a href="'.$crumb['link'].'">'.$crumb['title'].'</a>';
if (end($crumb)) {
echo ' '.l('divider').' ';
}
}
?>

</div><!-- End Breadcrumb Div -->
<div id="content">

8. Now let's make the s17/public/footer.tpl.php file.
Code: [Select]
</div><!-- End Content Div -->
<div id="footer">
<p>This site is powered by <a href="http://snewscms.com/" title="sNews CMS">sNews</a> | <?php login_link(); ?></p>
</div>
</div><!-- End Wrapper Div -->
<script type="text/javascript" src="tpl/s17/public/js/script.js"></script>
</body>
</html>

8a. Let's screate the Javascript file: s17/public/js/script.js now:  I am adding Jquery here...
Code: [Select]
// Onload
$(document).ready(function() {
// Search field
searchField();
});

function searchField() {
var kw = document.getElementById("keywords");
kw.onfocus = function() {
kw.value = '';
}
kw.onblur = function() {
if (kw.value == '') {
// put the "search_keywords" lang in the html as a title tag
kw.value = kw.getAttribute("title");
}
}
}

9. Since I am adding the search function, I will add the functions now.  

Go back to s17/public/index.php and make this the first case function:
Code: [Select]
case ( is_home() && isset($_POST['search_query']) ) :
include('search.tpl.php');
break;

9b. Let's make the search.tpl.php file:
Code: [Select]
<?php

// EQ Note - You probably will ask why I have the search result function here.  I think this is a addon type that should not included in the main system file.  I could of had this in the template functions.php file but somehow I choose not to...

// Search Results
/* 
$array = search_result( $str )
if isset($array['charerror'])  = Less than 4 characters or the query is for "Search Keywords"
if isset($array['no_results'])  = No results from the search query
if isset($array['num'])  = Number of results back
if isset($array['results'])  = Search Result information - array
*/
function search_results$str$limit 20 ) {
$arr = array();

$search_query clean(cleanXSS$str ));

if (strlen($search_query) < || $search_query == l('search_keywords')) {
$arr['charerror'] = '';
} else {

$keywords explode(' '$search_query);
$keyCount count($keywords);
$query '
SELECT 
a.id
FROM '
._PRE.'articles AS a
LEFT OUTER JOIN '
._PRE.'categories as c
ON category = c.id 
AND c.published =\'YES\'
LEFT OUTER JOIN '
._PRE.'categories as x
ON c.subcat =  x.id 
AND x.published =\'YES\'
WHERE position != 2
AND a.published = 1
'
._VIS.'
AND'
;
if ($keyCount 1) {
for ($i 0$i $keyCount 1$i++) {
$query $query.' (title LIKE "%'.$keywords[$i].'%" ||
text LIKE "%'
.$keywords[$i].'%" ||
keywords_meta LIKE "%'
.$keywords[$i].'%") &&';
}
$j $keyCount 1;
$query $query.'(title LIKE "%'.$keywords[$j].'%" ||
text LIKE "%'
.$keywords[$j].'%" ||
keywords_meta LIKE "%'
.$keywords[$j].'%")';
} else {
$query $query.'(title LIKE "%'.$keywords[0].'%" ||
text LIKE "%'
.$keywords[0].'%" ||
keywords_meta LIKE "%'
.$keywords[0].'%")';
}
$query $query.' ORDER BY id DESC LIMIT '.$limit;
$result mysql_query($query);
$numrows mysql_num_rows($result);
if (!$numrows) {
$arr['no_results'] = '';
} else {
$arr['num'] = $numrows;
while ($r mysql_fetch_array($result)) {
$Or_id[] = 'a.id ='.$r['id'];
}
$Or_id implode(' OR ',$Or_id);
$query 'SELECT
a.*,
c.name AS name,c.seftitle AS csef,
x.name AS xname,x.seftitle AS xsef
FROM '
._PRE.'articles'.' AS a
LEFT OUTER JOIN '
._PRE.'categories'.' as c
ON category = c.id
LEFT OUTER JOIN '
._PRE.'categories'.' as x
ON c.subcat =  x.id
WHERE '
.$Or_id;
$result mysql_query($query);
while ($r mysql_fetch_array($result)) {
// url link
if (isset($r['xsef'])) {
  $link $r['xsef'].'/'.$r['csef'].'/';
} else {
$link = isset($r['csef']) 
$r['csef'].'/' 
'';
}
$r['link'] = $link.$r['asef'];

$arr['results'][] = $r;
}
}
}
return $arr;
}

?>

<div id="main">
<h2><?php echo l(search_results); ?></h2>
<?php 
$result search_results$_POST['search_query'] );

// Input error
if (isset($result['charerror'])) {
echo '<p>There was an error in the input provided, please try again.</p>';

// No results
} elseif (isset($result['no_results'])) {
echo '<p>No results found for <strong>'.$_POST['search_query'].'</strong>.</p>';

// Results found
} elseif (is_array($result['results'])) {
echo '<p><strong>'.$result['num'].'</strong> '.l('resultsfound').' <strong>'.$_POST['search_query'].'</strong>.</p>';

foreach( $result['results'] as $rtnd ) {
echo '<p><a href="'._SITE.$rtnd['link'].'">'.$rtnd['title'].'</a> - '.format_date($rtnd['date']).'</p>';
}
}
?>

<br>
<p><a href="<?php echo _SITE?>"><?php echo l('backhome'); ?></a></p>
</div>

10.  Let's make the s17/public/home.tpl.php file
Code: [Select]
<?php

// Page
if( s('display_page') > ) {
include('single.tpl.php');
} else {
// Home list
include('category.tpl.php');
}

?>


11. Now I will create the "center" content

Add the following to s17/public/inc/functions.php.  Everything should be self explanitory, even the class here.
Code: [Select]
// EQ Note - this is not in the tpl.core.php file because I don't think the system will continue to have the break function.  Additionally, the content requested is user/dev based, so commentable and comment_num may not even be needed if desired.
class Content {
private $data = array();

public function __construct( $arr ) {
$this->data = $arr;
}

public function get( $var ) {
return $this->data[$var];
}

public function has_break() {
if (strpos($this->data['text'], '[break]') != 0) {
return TRUE;
}
}

public function is_commentable() {
if ($this->data['commentable'] == 'YES') {
return TRUE;
}
}

public function has_comments() {
if ($this->data['cmnt_num'] > 0) {
return TRUE;
}
}
}


// Gets list of content
// ex: content_list(s('article_limit'), cat_id());
function content_list( $qty, $category = '' ) {
static $arr;
if (!$arr) {
$arr = array();

// where clause for category or home
$category = !empty($category)
? 'AND category = '.$category
: '';

$showOnhome = (!cat_id() && position() != 3)
? " AND show_on_home = 'YES'"
: '';

$currentPage = page_num()
? page_num() - 1
: 0;

$query = '
SELECT
 a.*
, (SELECT count( id )
FROM comments
WHERE articleid = a.id
AND approved = \'TRUE\'
 ) AS cmnt_num
, c.name as cat_name
, c.seftitle as cat_sef
, x.name as parent_name
, x.seftitle as parent_sef
FROM '._PRE.'articles AS a
LEFT OUTER JOIN '._PRE.'categories as c
ON a.category = c.id
AND c.published =\'YES\'
LEFT OUTER JOIN '._PRE.'categories as x
ON c.subcat =  x.id
AND x.published =\'YES\'
WHERE a.position = 1
AND a.published = 1
'.$category.'
'.$showOnhome.'
ORDER BY artorder ASC, date DESC
LIMIT '.$currentPage * $qty.','.$qty;
$result = mysql_query($query);
while ($r = mysql_fetch_assoc($result)) {
$arr[] = $r;
}
}
return $arr;
}

// gets full article or page
// ex: content_single( content_id() )
function content_single( $id ) {
static $arr;
if (!$arr) {
$query = '
SELECT
 a.*
, (SELECT count( id )
FROM comments
WHERE articleid = '.$id.'
AND approved = \'TRUE\'
 ) AS cmnt_num
, c.name as cat_name
, c.seftitle as cat_sef
, x.name as parent_name
, x.seftitle as parent_sef
FROM '._PRE.'articles AS a
LEFT OUTER JOIN '._PRE.'categories as c
ON a.category = c.id
AND c.published =\'YES\'
LEFT OUTER JOIN '._PRE.'categories as x
ON c.subcat =  x.id
AND x.published =\'YES\'
WHERE a.published = 1
AND a.id = '.$id;
$result = mysql_query( $query );
while ($r = mysql_fetch_assoc( $result )) {
$arr = $r;
}
}
return $arr;
}

// EQ Notes - These are just wrappers of the main functions
function content_admin_links( $id, $vis ) {
$arr = array();
$arr[] = edit_link( $id );
$arr[] = visible_link( $id, $vis, currCategory() );
return $arr;
}

function cmnt_admin_links( $id ) {
$arr = array();
$arr[] = edit_cmnt_link( $id );
$arr[] = del_cmnt_link( $id );
return $arr;
}

// Pagination Links
function prevnext_links() {
$arr = array();
$arr = paginationLinks(
page_num(),
s('article_limit'),
maxArticleNum()
);
return $arr;
}

function cmnt_prevnext_links() {
$arr = array();
$arr = paginationLinks(
cmnt_page_num(),
s('comment_limit'),
maxCommentNum()
);
return $arr;
}

Now, create s17/public/single.tpl.php and add the following:
Code: [Select]
<div id="main">
<?php $content = new Content(content_singlecontent_id() )); ?>
<h2><?php echo $content->get('title'); ?></h2>

<?php echo parse_text($content->get('text')); ?>

<p class="date">
<?php echo format_date($content->get('date')); ?>

<?php if (_ADMIN) {
$adminLinks content_admin_links
  $content->get('id')
$content->get('visible'
);
foreach ( $adminLinks as $link ) {
echo ' '.l('divider').' ';
echo '<a href="'.$link['link'].'">'$link['title'].'</a>';
}
?>

</p>
<?php 
// Comments
if ( $content->has_comments() ) {
include('comments.tpl.php');
}
if ($content->is_commentable() ) {
include('comment_form.tpl.php');
}
?>

</div>
** Note, I am not including the comment_form as of yet - I am not done....

For a comment listing in s17/public/comments.tpl.php:
Code: [Select]
<div id="comment-section">
<h3>Comments</h3>
<?php foreach ( comment_list() as $comment ): ?>
<p class="meta">
<?php echo !empty($comment['url']) 
'<a href="'$comment['url'] .'" rel="nofollow">'$comment['name'] .'</a>'
$comment['name'];
?>

<a id="Comment<?php echo $comment['cmnt_num']; ?>" name="Comment<?php echo $comment['cmnt_num']; ?>"></a>
on <?php echo format_date($comment['time']); ?>

<?php if (_ADMIN) {
$adminLinks cmnt_admin_links$comment['id'] );
foreach ( $adminLinks as $link ) {
echo '<a href="'.$link['link'].'">'$link['title'].'</a>';
if (end($adminLinks) != $link) {
echo ' '.l('divider').' ';
}
}
?>

</p>
<div class="comment">
<?php echo $comment['comment']; ?>
</div>
<?php endforeach; ?>

<?php 
// Pagination
if (s('display_pagination') == 'on') :
$cmnt_pg cmnt_prevnext_links();
// If the pagination links are not empty...
if (!empty($cmnt_pg)) :
$cmnt_pg['url'] = str_replace
l('comment_pages').cmnt_page_num(),  // probably should be get_uri('c_paginSEF')
''
curr_url() 
);
?>

<div class="paginator">
<?php 
echo $cmnt_pg['previous-page'
'<a href="'.$cmnt_pg['url'] . l('comment_pages') . $cmnt_pg['previous-page'].'">Newer Comments</a>' 
'';
?>


Page <strong><?php echo cmnt_page_num() ? cmnt_page_num() : 1?></strong> of <?php echo $cmnt_pg['last-page']; ?>

<?php echo $cmnt_pg['next-page'
'<a href="'.$cmnt_pg['url'] . l('comment_pages') . $cmnt_pg['next-page'].'">Older Comments</a>'
'';
?>

</div>
<?php endif; 
endif;
?>

</div>

Now, the category.tpl.php code:
Code: [Select]
<div id="main">
<?php foreach (content_list(s('article_limit'), cat_id()) as $post):
$content = new Content($post);

$link $content->get('parent_sef')
$content->get('parent_sef').'/'.$content->get('cat_sef'
$content->get('cat_sef');
?>

<h2><a href="<?php echo $link.'/'.$content->get('seftitle'); ?>"><?php echo $content->get('title'); ?></a></h2>

<?php echo parse_text($content->get('text')); ?>

<p class="date">
<?php 
// Read More Link
if ( $content->has_break() ) {
echo '<a href="'$link.'/'.$content->get('seftitle') .'">'.l('read_more').'</a>';
echo ' '.l('divider').' ';
}
?>


<?php 
// Comments
if ( $content->has_comments() ) {
echo '<a href="'._SITE.$link.'/'.$content->get('seftitle').'/#'.l('comment').'1">
'
.l('comments').' ('.$content->get('cmnt_num').')</a>';
echo ' '.l('divider').' ';
}
?>


<?php echo format_date($content->get('date')); ?>

<?php if (_ADMIN) {
$adminLinks content_admin_links
  $content->get('id')
$content->get('visible'
);
foreach ( $adminLinks as $link ) {
echo ' '.l('divider').' ';
echo '<a href="'.$link['link'].'">'$link['title'].'</a>';
}
?>

</p>
<?php endforeach; ?>

<?php if (s('display_pagination') == 'on') :
$pagin prevnext_links();

// If the pagination links are not empty...
if(!empty($pagin)) :

$pagin['url'] = str_replace
l('paginator').page_num(), 
''
curr_url() 
);
?>

<div class="paginator">
<?php 
echo $pagin['previous-page'
'<a href="'.$pagin['url'].l('paginator').$pagin['previous-page'].'">Newer Entries</a>' 
'';
?>


Page <strong><?php echo page_num() ? page_num() : 1?></strong> of <?php echo $pagin['last-page']; ?>

<?php echo $pagin['next-page'
'<a href="'.$pagin['url'].l('paginator').$pagin['next-page'].'">Older Entries</a>'
'';
?>

</div>
<?php endif; ?>
<?php endif; ?>
</div>

This is the end so far.  I don't have the sidebar,comment's form, or error page done atm, but it will be soon.  

Part of the sidebar would have been similar to the first post, with the RSS links.  An Example for categories would be:
function.php:
Code: [Select]
// Categories
function cat_list() {
$arr = array();
$query = 'SELECT
id, seftitle, name as title, description, subcat
FROM '._PRE.'categories
WHERE subcat = 0
AND published = \'YES\'
AND (SELECT sum(id) FROM '._PRE.'articles WHERE category = categories.id
AND position = 1
AND published = 1 '._VIS.'
) > 0
GROUP BY id
ORDER BY catorder, id';
$result = mysql_query($query);
while ($r = mysql_fetch_assoc($result)) {
if ($r['seftitle'] == get_uri('categorySEF')) {
$squery = 'SELECT
id, seftitle, name as title, description, subcat
FROM '._PRE.'categories
WHERE subcat = '.$r['id'].'
AND published = \'YES\'
AND (SELECT sum(id) FROM '._PRE.'articles WHERE category = categories.id
AND published = 1 '._VIS.'
) > 0
GROUP BY id
ORDER BY catorder, id';
$sresult = mysql_query($squery);
while ($s = mysql_fetch_assoc($sresult)) {
$r['children'][] = $s;
}
}
$arr[] = $r;
}
return $arr;
}

and the sidebar.tpl.php
Code: [Select]
<h3>Categories</h3>
<ul>
<?php foreach (cat_list() as $cat) {
echo '<li><a href="'._SITE.$cat['seftitle'].'/">'.$cat['title'].'</a>';
if (isset($cat['children'])) {
echo '<ul>';
foreach ($cat['children'] as $subcat) {
echo '<li class="subcat"><a href="'._SITE.$cat['seftitle'].'/'.$subcat['seftitle'].'/">'.$subcat['title'].'</a></li>';
}
echo '</ul>';
}
echo '</li>';
?>

</ul>
« Last Edit: July 10, 2010, 09:08:08 PM by nukpana »
Logged