News:

Want to get involved in developing SMF, then why not lend a hand on our github!

Main Menu

Simple Machines Website-Forum Integration

Started by Thantos, April 09, 2007, 06:51:32 PM

Previous topic - Next topic

Thantos

We get asked quite a bit about which CMS we use here at Simple Machines.  The answer is that we don't!  We use the SSI.php file that is included with SMF.  However we wrap SSI.php in another file called integrate.php in order to simply the entire process.

The goals when setting up the integration was:

  • Make full use of SMF's layer and template system.
  • Consolidate redundant code into a central file.
  • Make each file easy to create and update.
  • Allow full flexibility while doing the above.
Each file is broken up into 4 main parts

  • Setup variables
  • Bringing in integrate.php
  • A source function
  • A template function
The integrate.php file can handle many variables — the current version has 10 —  but most pages use two main variables.  These variables mainly set the default values so it doesn't need to override them later.

Bringing in integrate.php is simply a matter of using a require_once() with the path to the file.

The source function can do anything a normal SMF source function can do.  So it may query the database to get some data, do some calculations, redirect to another page, whatever is needed.  Just like in SMF it is important to keep your source type of actions — collecting and organizing data, doing calculations, doing massive queries, etc — from your display.  However not every file actually needs a source function, for example our About page just outputs text, so a setup variable was setup in order to turn that off.

The template function is what actually displays whatever it is we want to display.  It is called just like a SMF template function is called.

integrate.php
The integrate.php file is where the majority of the complex code is located, even then its not that complex.  So let me break it down for you.

<?php
global $siteurl;
$siteurl 'http://www.simplemachines.org';
$context['siteurl'] = $siteurl;

Here we just want to setup a variable that stores where our website is located.  Useful for when you need to output a URL.


if (basename($_SERVER['PHP_SELF']) == 'integrate.php')
{
header('Location: ' . $siteurl);
die;
}

The integrate.php file doesn't actually display anything useful if you navigate to it.  In fact it would cause some errors since the source and template functions aren't defined.  As such we just want to redirect people away from the file.


$ssi_theme = 10;
$context['outside_forum'] = true;
$ssi_maintenance_off = true;
require('ABSOLUTE PATH/community/SSI.php');

Here we setup a few variables for SSI.php itself.  The ssi_theme variable sets the actual theme to use while the $ssi_maintenance_off means that even if the forum is in maintenance mode the site will still work.  The $context['outside_forum'] is used by the templates that serve both the website and the forum in order to change output depending on location.


if (!empty($redirect))
redirectexit(str_replace('{SITEURL}', $siteurl, $redirect));

We have a couple of files that we keep around for legacy purposes, but that are covered in other files.  So we just redirect the user to those files.


// Is this page restricted?
if (!empty($pagePermissions))
isAllowedTo($pagePermissions);

This allows use to restrict access to a particular page by permission.  I don't think I've used it yet, but it's nice to know the functionality is there if I want to use it.


if (!empty($pageIsIndex) && empty($user_info[$pageIsIndex]))
redirectexit($siteurl);

We have some pages that are for the team only and this provides an easy way to limit access to those pages.


// Ok we are outside of the forum so load up some more stuff.
loadTemplate('SiteTools');

There are some functions that we only need when outside of the forum, so we put them into their own template file and load it when we need to.


$callback = isset($callback) && $callback === false ? false : (!empty($callback) ? $callback : 'file_source');

This sets the name of the source function  The first thing it checks for is if the variable was set to false, if that is true that means the file doesn't need a source function.  Otherwise it looks to see if the callback function name was given and if so uses that, if not it uses the default name, which most files use.


$context['sub_template'] = !empty($template) ? $template : 'file_template';

Just like the source function except in this case there is no option to not set a template function.


if (!empty($section))
$context['section'] = $section;

$context['section'] sets the active part of the site wide menu.  If no value is specified the menu function itself sets a default.


if (!empty($menulink))
$context['menulink'] = $menulink;

On pages with a side menu, this sets the active menu item.  If none is set then it doesn't select an active link.


if (!empty($sectionheader))
$context['sectionheader'] = $sectionheader;

The section header is the text that is above the menu bar and below the user bar.


$pagetxtIndex = isset($pagetxtIndex) ? $pagetxtIndex : (!empty($menulink) ? $menulink : '');

One of our goals is to have our entire site using a language file which might one day be translated.  So this provides a way of selecting the set of strings for this particular page.  Normally the menulink is enough, but this provides a way to override it (for example pages that share the same menu link).


if (!empty($section) && !empty($pagetxtIndex) && isset($sitetxt[$section][$pagetxtIndex]))
{
$pagetxt = $sitetxt[$section][$pagetxtIndex];
}

Our text strings are stored in a multiple dimension array.  In order to make using them easier we map $pagetxt to a particular $sitetxt sub array.  But we only do this if the needed information is provided.


$context['page_title'] = !empty($title) ? $title : (!empty($pagetxt) && !empty($pagetxt['title']) ? $pagetxt['title'] : 'Simple Machines LLC');

This simply sets the page title.  This actually allows two easy ways of setting the page title:  give $title a value or have a title index in the pagetxt variable.  The "Simple Machines LLC" is just there as a backup, so we'll always have a page title.


if (empty($noSideBar))
$context['template_layers'][] = 'menubar';

This allows us to not include the menu bar — like the home page.  The menu bar itself is just another layer that is included.  This makes the page itself really clean and also helps ensure consistency.


if ($callback !== false)
call_user_func($callback);

Unless we already specified we don't have a source function we call it here and get to the good stuff.


obExit();
?>

obExit is responsible for calling the templates in the correct order and it also serves as an ending point to the script.  Also obExit provides a some functionality that is important to do in SMF 2.0.

Some examples
Ok now that I've hit you with the hard stuff lets take a look at two pages that use it.  The first is index.php for our home page.

<?php

$noSideBar 
true;
$section 'index';
$pagetxtIndex 'home';

This is all of our setup variables.  We turn off the side bar, set the section and pagetxt index.


require_once('integrate.php');

This starts the whole process.


function file_source()
{
global $context, $db_prefix, $smfFunc;

$request = $smfFunc['db_query']('', "
SELECT
m.smileys_enabled, m.poster_time, m.id_msg, m.subject, m.body, t.id_topic, t.id_board,
b.name AS bname, t.num_replies, m.id_member, mem.real_name AS poster_name
FROM {$db_prefix}topics AS t, {$db_prefix}messages AS m, {$db_prefix}members AS mem, {$db_prefix}boards AS b
WHERE t.id_board = 1
AND t.is_sticky = 1
AND b.id_board = 1
AND m.id_msg = t.id_first_msg
AND mem.id_member = m.id_member
ORDER BY t.id_topic DESC
LIMIT 4", __FILE__, __LINE__);

while ($row = $smfFunc['db_fetch_assoc']($request))
{
$bodylen = 200;
// Limit the length of the message, if the option is set.
if (strlen(str_replace('<br />', "\n", $row['body'])) > $bodylen)
$row['body'] = htmlspecialchars(substr(str_replace('<br />', "\n", un_htmlspecialchars($row['body'])), 0, $bodylen-3) . '...', ENT_QUOTES);

$row['body'] = parse_bbc($row['body'], $row['smileys_enabled']);

censorText($row['body']);
censorText($row['subject']);

$context['smfinfo'][] = array(
'title' => $row['subject'],
'link' => $scripturl . '?topic=' . $row['id_topic'] . '.0',
'description' => $row['body'],
'author' => !empty($row['id_member']) ? $scripturl . '?action=profile;u=' . $row['id_member'] : '',
'category' => $row['bname'],
'comments' => $scripturl . '?action=post;topic=' . $row['id_topic'] . '.0',
'pubDate' => gmdate('D, d M Y H:i:s T', $row['poster_time']),
'guid' => $row['id_msg']
);
}

$smfFunc['db_free_result']($request);


if (!$user_info['is_team'] && !$user_info['is_charter'])
{
$context['welcome_title'] = $pagetxt['welcome']['public']['title'];
$context['welcome_text'] = $pagetxt['welcome']['public']['text'];
}
elseif ($user_info['is_team'])
{
$context['welcome_title'] = $pagetxt['welcome']['team']['title'];
$context['welcome_text'] = $pagetxt['welcome']['team']['text'];
}
elseif ($user_info['is_charter'])
{
$context['welcome_title'] = $pagetxt['welcome']['charter']['title'];
$context['welcome_text'] = $pagetxt['welcome']['charter']['text'];
}


// the main header
$context['sectionheader'] = '<img src="' . $context['siteurl'] . '/site_images/header24.png" />';
}

This is the source function.  Get gets the last 4 topics from the News board that are stickied and gives a short preview of them.  The next set of code is simply setting up some text depending on which of the three main groups we are targeting (team, charter members, and regular members).


function template_file_template()
{
global $context, $siteurl, $sitetxt, $txt, $pagetxt;

echo '
<div id="secondarybody" style="margin-left: 0;">
<div id="quicknav">
<a href="/download/" title="', $pagetxt['download_title'], '"><img src="site_images/download.png" style="margin-top: 0;" alt="', $pagetxt['download_title'], '" /><br />', $pagetxt['download'], '</a><br />
<a href="http://custom.simplemachines.org/mods" title="', $pagetxt['custom_title'], '"><img src="site_images/custom.png" alt="', $pagetxt['download_title'], '" /><br />', $pagetxt['custom'], '</a><br />
<a href="/about/features.php" title="', $pagetxt['features_title'], '"><img src="site_images/features.png" alt="', $pagetxt['download_title'], '" /><br />', $pagetxt['features'], '</a><br />
<a href="/support/" title="', $pagetxt['support_title'], '"><img src="site_images/support.png" alt="', $pagetxt['download_title'], '" /><br />', $pagetxt['support'], '</a>
</div>
<div id="lower">
<h3>', $pagetxt['h2'], '</h3>
<p>', $pagetxt['p1'], '</p><br />
<div id="splash_left">
<div id="splash"></div>
</div>
<div id="lower_bottom">
<div id="newsheaders">
<h3>', $pagetxt['h3'], '</h3>';

foreach($context['smfinfo'] as $info)
echo '
<dl>
<dt><a href="', $info['link'], '">', $info['title'], '</a></dt>
<dd>', $info['description'], '</dd>
</dl>';

echo '
</div>
<div id="memberbox" class="clearfix">
<div id="loginbox">
<h3>User</h3>';

if ($context['user']['is_logged'])
{
echo '
<h4>', sprintf($pagetxt['welcome_user'], $context['user']['name']), '</h4>
<ul>
<li>';
// Show how many messages there are
if ($context['user']['unread_messages'] == '0')
{
echo $pagetxt['no_new'];
}
elseif($context['user']['unread_messages'] == '1')
{
echo sprintf($pagetxt['one_new'], $context['user']['unread_messages']);
}
else
{
echo sprintf($pagetxt['many_new'], $context['user']['unread_messages']);
}
echo '
</li>
<li>', $pagetxt['go_profile'], '</li>
<li>', $pagetxt['unread'], '</li>
</ul>';
}
else
{
echo '
<h4>', $pagetxt['login'], '</h4>
<form action="http://www.simplemachines.org/community/index.php?action=login2" method="post">
<label for="user">', $txt['username'], ':</label>
<input type="text" id="user" name="user" size="9" value="" />
<label for="passwrd">', $txt['password'], ':</label>
<input type="password" name="passwrd" id="passwrd" size="9" />
<input type="hidden" name="cookielength" value="-1" />
<input type="submit" value="', $txt['login'], '" />
</form>';
}


echo ' </div>
<div id="search">
<h3>Search</h3> ';
theme_searchbox(false);
echo'
</div>
</div>
</div>
</div>
</div>';
}

?>

This is the template function.  You should notice it looks a lot like a normal SMF template function.

The next file is the about page index.
<?php

$section 
'about';
$menulink 'about';

$sectionheader 'About Simple Machines';
$title 'Who are we?';

$callback false;

Again all of our setup variables.  In this case we are manually setting the title.  This page is all HTML, so there is no need for a source function, so we turn it off.


require_once('../integrate.php');

By request of our server admin we are now using relative paths to get to integrate.php.


function template_file_template()
{
global $siteurl, $context;

echo '
<div class="snav">
<h6>Navigation</h6>
<ul class="snav">
<li  id="active" class="first"><a href="index.php">Who are we?</a></li>
<li><a href="values.php">Core Values</a></li>
<li><a href="whyfree.php">Free: it\'s better!</a></li>
<li><a href="opensource.php">Simple Machines and Open Source</a></li>
</ul>
</div>
<h3>Who are we?</h3>
<img class="img-floatright" src="../site_images/yabbse.png" alt="YabbSE" />
<h4>Past</h4>
<p>SMF can trace its roots all the way back to a perl powered message board, YaBB. After awhile, there became a demand for a php coded version of YaBB. So that is where YaBBSE comes into play. While YaBBSE was getting bigger and bigger, there were certain aspects of it that just needed improvement and reworking. The decision was made that it was best to separate from YaBBSE because it was a lot different from YaBB and it was best to start from scratch. At this point, SMF started being developed. On September 29th, 2003, the first beta of SMF was released to charter members, SMF 1.0 Beta 1. While this was a huge milestone for SMF, only charter members had access to use it. But on March 10, 2004, SMF made its public debut with the first public SMF release available to everyone.</p>

<h4>Present</h4>

<p>The people behind SMF are a diverse group of individuals who in their free time, put their efforts together to help make SMF what it is today, and help SMF take strides into the future. Many different uses of skills are expressed within several teams, including a design team, modification team, development team, documentation team, support team, and a language team. Each team works together to help SMF grow and reach its full potential. We are people, just like you, who put our efforts into SMF, driven by passion we have for the software. For a listing of all the team members who have helped put together the latest release of SMF, see the <a href="http://www.simplemachines.org/about/team.php">team page</a>.</p>

<h4>Future</h4>
<p>SMF strives to get better and better. Active discussions amongst the team are being made to discuss the future of SMF. In the future, you can expect SMF to be even more user-friendly, offer better and quicker support, and much more. This is just the beginning for SMF, expect the building blocks of SMF and the community to be expanded, but to keep to the original goal: Simple, elegant, powerful, and free.</p>

<div class="snav">
<h6>Navigation</h6>
<ul class="snav">
<li id="active" class="first"><a href="index.php">Who are we?</a></li>
<li><a href="values.php">Core Values</a></li>
<li><a href="whyfree.php">Free: it\'s better!</a></li>
<li><a href="opensource.php">Simple Machines and Open Source</a></li>
</ul>
</div>';
}

?>

This page hasn't been changed to use $pagetxt but hopefully one day ;)


I hope this rather lengthy post will help you all understand how we have it setup and hopefully will help in doing the same on your site if you wish.

Daniel15

Very nice post, Thantos! A lengthy post, but some great reading. You've given me some ideas on how to restructure my site's (rather ugly) code. :D


QuoteThis allows use to restrict access to a particular page by permission. 
Now there's an awesome idea :)

Quote$request = $smfFunc['db_query']('', "
Just as a note for anyone looking at the code posted, $smfFunc['db_query'] is SMF 2.0's equivalent to db_query(). Similarly, $smfFunc['db_fetch_assoc'] is SMF 2.0's mysql_fetch_assoc(). It's done like this in SMF 2.0 due to the multi-database support (the $smfFunc functions are basically wrappers for the proper database functions - If you're using MySQL, $smfFunc['db_fetch_assoc'] calls mysql_fetch_assoc. Likewise, if you're using SQLite, it calls SQLite's equivalent. Quite a smart idea, and works really well :))
Daniel15, former Customisation team member, resigned due to lack of time. I still love everyone here :D.
Go to smfshop.com for SMFshop support, do NOT email or PM me!

webs86

#2
hi! this version of integrate.php file work correctly with SMF 1.1.2 or it requires SMF 2.0 version?

because I have an error with smf 1.1.2 and integrate.php.
at this line
$request =  $smfFunc['db_query']('', "

i returned this error:
Fatal error: Function name must be a string in C:\Program Files\xampp\htdocs\picosafra\index.php on line XX


Thanks

Daniel15

Quote from: webs86 on April 24, 2007, 04:58:42 PM
hi! this version of integrate.php file work correctly with SMF 1.1.2 or it requires SMF 2.0 version?

because I have an error with smf 1.1.2 and integrate.php.
at this line
$request =  $smfFunc['db_query']('', "

i returned this error:
Fatal error: Function name must be a string in C:\Program Files\xampp\htdocs\picosafra\index.php on line XX


Thanks

Please read my post above ;)
$request $smfFunc['db_query'](''"
needs to be changed to
$request db_query("

The same for $smfFunc['db_fetch_assoc'](; it needs to be changed to mysql_fetch_assoc(
Daniel15, former Customisation team member, resigned due to lack of time. I still love everyone here :D.
Go to smfshop.com for SMFshop support, do NOT email or PM me!

ladynada

although totally not sophisticated like your code, I also used SMF code to make the ultiportal portal.  its working fine for me.  I will have to look into your code now for some hacks to make ultiportal better.

:)

nada
WORK for Truth, Print it, Take Time to READ ALL LINKS NOTED  click here --> The TWO Witnesses are Mom and Dad and SMF Skins

cyberox

Does anyone have a working sample zip file for this?
I've followed each step; but still cannot integrate custom pages into smf.

Daniel15

Quote from: cyberox on May 29, 2007, 05:35:32 PM
Does anyone have a working sample zip file for this?
I've followed each step; but still cannot integrate custom pages into smf.
To add a page "into" SMF, try this code in a seperate PHP file:

<?php
error_reporting
(E_ALL);

// Theme we're going to use
$ssi_theme 4;
// Layers we're going to use
$ssi_layers = array('main');
// Enable GZip compression (saves bandwidth)
$ssi_gzip true;

require(
'SSI.php');

echo 
'See? SMF is cool :)';

ssi_shutdown();
?>


You'll need to change the $ssi_theme = 4 line to the correct theme ID.
Daniel15, former Customisation team member, resigned due to lack of time. I still love everyone here :D.
Go to smfshop.com for SMFshop support, do NOT email or PM me!

lwjuan

I do not know about php scripting.. So is it i have to create a new php file and copy the above content and edit some and upload to the server then it'll work?

Thantos

To be honest, if you don't understand programming/scripting then my files will most likely be more then you need.

Daniel15

Quote from: lwjuan on June 07, 2007, 10:02:11 PM
I do not know about php scripting.. So is it i have to create a new php file and copy the above content and edit some and upload to the server then it'll work?
As Thantos said, his post is mainly aimed towards PHP developers...
I'd suggest to use my code above (or rather, a slightly modified version of it):

<?php
error_reporting
(E_ALL);

// Theme we're going to use
$ssi_theme 4;
// Layers we're going to use
$ssi_layers = array('main');
// Enable GZip compression (saves bandwidth)
$ssi_gzip true;

require(
'SSI.php');

?>

Put your HTML content here!
<?php

ssi_shutdown
();
?>



I guess the easiest way is to create two new files: header.php, and footer.php.

header.php:

<?php
error_reporting
(E_ALL);

// Theme we're going to use
$ssi_theme 4;
// Layers we're going to use
$ssi_layers = array('main');
// Enable GZip compression (saves bandwidth)
$ssi_gzip true;

require(
'SSI.php');

?>



footer.php:

<?php
ssi_shutdown
();
?>



Then, in a new PHP file (name it whatever you want):

<?php require('header.php'); ?>

Your HTML stuff would go here!<br />
etc etc etc.

<?php require('footer.php'); ?>


This is not really the best way, but it's the easiest way for non-programmers to achieve a good result :)
Daniel15, former Customisation team member, resigned due to lack of time. I still love everyone here :D.
Go to smfshop.com for SMFshop support, do NOT email or PM me!

maurizio35

Hey Daniel15, thanks for the code, I really appreciated it, but I have 2 simple questions for you about the code.

Do you know what I should do to make this page accessible to members only? (require a login?)

And how can I write the username and email address of the user to somewhere in that page? (what codes I should use?)

Regards,
Maurizio.

dextrous

Quote
// Layers we're going to use
$ssi_layers = array('main');
Hmm, themes I understand. What are layers?

Daniel15

#12
QuoteDo you know what I should do to make this page accessible to members only? (require a login?)
If you want the SMF login box when a guest tries to access the page, put this at the very top of the page:

is_not_guest('Error: You must log in to access this section!');


If you just want to check whether they're a guest or not (and not show the login box), you'd do something like this:

<?php
if ($context['user']['is_guest'])
echo 'Error: You must be logged in to access this section!';
else
{
// The stuff that members can see here
}
?>



QuoteAnd how can I write the username and email address of the user to somewhere in that page?
Their username is in $context['user']['name'], and the email address should be in $context['user']['email'] (although I did not check). To use these in an echo statement, do something like:

<?php
echo '
bla bla bla...<br /><br />

Your username: '
$context['user']['name'], '<br />
Your email address: '
$context['user']['email'], '<br />

bla bla bla...
....'
;
?>



To see all the $context['user'][...] values you can use:

<?php
echo '<pre>'print_r($context['user'], true), '</pre>';


QuoteHmm, themes I understand. What are layers?
Essentially, layers are two sub-templates: A top one (above), and a bottom one (below). The "above" layer is displayed above your content, whilst the "below" layer is displayed below it. For example, the "main" layer has "template_main_above" and "template_main_below" as its two sub-templates.

By default, a SMF theme will have a single layer, the "main" layer. This is the template_main_above and template_main_below functions in index.template.php So, your document flow will be like this:
- template_main_above (the "above" bit of the main layer)
- [ Your content ]
- template_main_below (the "below" bit of the main layer)

You may add additional layers, and they will appear in the order specified. For example, if you have two layers called "html" and "body", the document flow will be like this:
- template_html_above
- template_body_above
- [ Content ]
- template_body_below
- template_html_below

Hope I explained it well :). If not, I'll try and post sometihng that's easier to understand :P

Edit: Some definitions:

Sub-template: A single function (template_*) inside a template file.
Template: A single template file (eg. index.template.php, Display.template.php) comprised of multiple sub-templates
Theme: A directory consisting of a collection of templates
Layer: Two sub-templates, an "above" one and a "below" one
Daniel15, former Customisation team member, resigned due to lack of time. I still love everyone here :D.
Go to smfshop.com for SMFshop support, do NOT email or PM me!

Karuhun

Great tutorials, thanks to Thantos and Daniel15 :)

Daniel15

Daniel15, former Customisation team member, resigned due to lack of time. I still love everyone here :D.
Go to smfshop.com for SMFshop support, do NOT email or PM me!

Dragooon

Ahh I loved it!
It helped me to get 5 latest news from my news board :P

Miles Marshall

I want to make a homepage, About me etc.. using custom SSI could someone point me in the right direction? I want to start off with the "About me page' Like smf has on here all tied together neatly I created a

header.php
<?php
error_reporting
(E_ALL);

// Theme we're going to use
$ssi_theme 1;
// Layers we're going to use
$ssi_layers = array('main');
// Enable GZip compression (saves bandwidth)
$ssi_gzip true;

require(
'SSI.php');


footer.php
<?php
ssi_shutdown
();
?>


well then my page of course home.php when I navigate to the page I get errors
http://geekinc.info/home.php
Need Computer Help? Just want to chat? Come on over! http://www.geekinc.org
"I tried to fix the world but they wouldn't give me the source code"

-Miles

Coldfx

Quote from: Miles™ on August 23, 2007, 11:33:32 AM
I want to make a homepage, About me etc.. using custom SSI could someone point me in the right direction? I want to start off with the "About me page' Like smf has on here all tied together neatly I created a

header.php
<?php
error_reporting
(E_ALL);

// Theme we're going to use
$ssi_theme 1;
// Layers we're going to use
$ssi_layers = array('main');
// Enable GZip compression (saves bandwidth)
$ssi_gzip true;

require(
'SSI.php');


footer.php
<?php
ssi_shutdown
();
?>


well then my page of course home.php when I navigate to the page I get errors
http://geekinc.info/home.php


<?php

$ssi_gzip 
true;
$ssi_ban true;
ob_start();

require(
'SSI.php');

$context['page_title'] = 'Title Here';

template_main_above();

echo
'Content Here';

template_main_below();

?>



That's what I use.

Miles Marshall

So ok lets start over, Tell me how I  would make a custom page from scratch
Need Computer Help? Just want to chat? Come on over! http://www.geekinc.org
"I tried to fix the world but they wouldn't give me the source code"

-Miles

Miles Marshall

I just want to make new pages, Like a home page and what not the right way
Need Computer Help? Just want to chat? Come on over! http://www.geekinc.org
"I tried to fix the world but they wouldn't give me the source code"

-Miles

Advertisement: