[title sub="Written by Chris Graham"]Composr Tutorial: Integration of Composr and other installed scripts/applications[/title]

This tutorial will provide details on how to integrate Composr with another web system (known henceforth as the 'other system') installed for your website.

Please note that many systems, especially complex ones, will not easily integrate without reprogramming. In this event, you may wish to hire a company for professional assistance. Even the kinds of integration here usually require a high level of skill in web technologies and Composr technologies.

[contents]decimal,lower-alpha[/contents]

[title="2"]Linking[/title]

The simplest form of integration is by simply placing a link on your menu. There is nothing stopping you having multiple systems installed on your website as long as they do not have conflicting file/directory structures.

[title="2"]Embedding a simple HTML page[/title]

If you don't need to integrate a full web application, just a simple HTML page, this can be done without much trouble.

[title="3"]The careful way[/title]

The two main issues that present themselves are:
1) HTML files tend to have associated data files, and the path these are read from when integrated into Composr will not be the same as the path of the HTML file itself (as page files are located in a different location to where the index.php file that handles the page request is located)
2) HTML files contain surrounding mark-up that must be stripped, because Composr already provides it

[box="Note" width="25em" float="right"]
Relative URLs are normally read relative to the path the referencing page is in. However, this behaviour can be changed using an HTML tag [tt]base[/tt], but Composr does not use this by default; you may place a [tt]base[/tt] into Composr in [tt]HTML_HEAD.tpl[/tt] if you wish, to create a common location for relative-URLs to be relative to. This is possible in Composr as Composr itself only uses absolute URLs.
[/box]

The best way I can explain how to integrate an HTML page is by presenting a simple scenario.

In this scenario, a HTML page named [tt]mypage.html[/tt] has been made in an editor, and it contains a file [tt]myimage.png[/tt] that is referenced by a relative URL with no path (i.e. it is assumed to be in the same directory as the [tt]mypage.html[/tt] file).

Imagine [tt]mypage.html[/tt] contains...
[code="HTML"]
<html>
	<head>
		<title>This is my page</title>
	</head>
	<body>
		<img src="myimage.png" />
	</body>
</html>
[/code]

To integrate this page as [tt]site:mypage[/tt], the following steps would need to be taken:
 - Strip down the file to only contain [tt]<img src="myimage.png" />[/tt].
 - Place [tt]myimage.png[/tt] in the [tt]site[/tt] directory (this assumes that 'URL Schemes' are turned off -- if you have a URL Scheme enabled then it is best to just use absolute URLs instead)
 - Rename the file to [tt]mypage.htm[/tt] (HTML pages in Composr must end [tt].htm[/tt], not [tt].html[/tt] or anything else)
 - Place [tt]mypage.htm[/tt] in [tt]site/pages/html_custom/EN/[/tt]

[title="3"]The magical way[/title]

Composr can actually handle most things for you if you want to defer control to it:
[list="1"]
[*] Copy any [tt].htm[/tt] files into the [tt]pages/html_custom/EN/[/tt] directory ([tt].html[/tt] files should be renamed to [tt].htm[/tt] files)[list]
 [*][i]Unless[/i] the file is meant to be located in a subdirectory and Composr has a zone with the same name as that subdirectory. In this case, you'd place it in the equivalent directory of that zone so that Composr can better automatically rewrite any links to the page:[list]
  [*] For example, don't upload [tt]pages/html_custom/EN/example/test.htm[/tt] if you have a zone named [tt]example[/tt]. Put the file in [tt]example/pages/html_custom/EN/[/tt]
  [*] If the directories go more than one level deep, Composr will translate [tt]/[/tt] to [tt]_[/tt] when matching against a zone name (e.g. [tt]site/more[/tt] would be considered a zone named [tt]site_more[/tt])
[/list][/list]
[*] Copy any non-.htm files (images, etc) into [tt]uploads/website_specific[/tt]
[*] Your pages should then show up as normal Composr entry-points in the menu editor
[*] Go through the pages in Composr to find any broken links and adjust them so that they work. Composr can't perfectly fix your links for you every-time, but it does do a pretty good job. Read on for more information how link rewriting works.
[/list]

Composr link alternation works by replacing simple patterns to local relative links. For example, a link [tt]site/mypage.htm[/tt] would go to the Composr [tt]site:mypage[/tt] entry point. If the [tt]mypage.htm[/tt] file had been correctly copied to [tt]pages/html_custom/EN/[/tt] then Composr would actually load it up as intended.
A link [tt]mypage.htm[/tt] would go to the Composr [tt]:mypage[/tt] entry point.

As Composr can only rewrite the simple links, the process of fixing broken links is often a matter of simplifying them. For example:
 - [tt]http://yourbaseurl/mypage.htm[/tt] could not be fixed by Composr, because it is not a local link. Change it to [tt]mypage.htm[/tt].
 - [tt]../mypage.htm[/tt] might be used from a page located in a subdirectory. Unfortunately Composr can't realistically recognise this, so it can't translate this link. In this case, you would need to replace the link with the proper Composr page-link.

To clarify, the following linking situations confuse Composr:
 - full links to local pages
 - page trees that go more than one level deep
 - links that go back up a page tree

If you need advanced parsing functionality, whether to rewrite links/HTML in other ways or to do other processing, you can write hooks under [tt]sources_custom/hooks/systems/site_html_pages[/tt]. It accepts a run() method which receives the parameters $html (passed by reference, so you can modify the HTML output), $string (relative path to the page), and $file_base (base path used to access the page).

[title="2"]Embedding into Composr[/title]

If you wish for the other system to appear directly inside Composr, much like a Composr page, this is a lot more awkward.

[title="3"]iframe[/title]

[media width="150" description="Creating a new Comcode page to place the iframe" float="right"]data_custom/images/docs/tut_integration/integration_cc_page.png[/media]
An iframe is an HTML construct that allows you to place one site inside a region ('frame') of another. There are a few main drawbacks with frame based approaches however:
 - the browser back button will send the whole Composr site back, not the embedded site. In other words, if you have made clicks inside the embedded system, and then click 'back' in your browser, Composr will move back, with the likely result being the embedded section is no longer the display Composr page
 - sometimes web browser bugs can cause rendering problems, especially when it comes to scrollbars
 - the title-bar titles would not be reflected in the browser title-bar

To place an iframed system into Composr, the easiest way is to make a new Comcode page which will contain the frame, link that onto your menu, and place the following Comcode into the page:
[code="Comcode"]
[html]
<iframe frameBorder="0" scrolling="no" title="whatever the embedded system is" src="whatever the system URL is" style="width: 100%; height: whatever height you want px;">whatever the embedded system is</iframe>
[/html]
[/code]

Where the following are appropriately replaced:
 - whatever the embedded system is (e.g. 'Something web system')
 - whatever the system URL is (e.g. [tt]{$BASE_URL}/somethingwebsystem/[/tt])
 - whatever height you want (e.g. '900')

If the embedded system has a non-predictable height, then under normal circumstances, an extra set of scroll-bars would be rendered around it when your pre-set height is exceeded. In order to avoid this without needing to choose an excessive default height, special code must be written that will regularly resize the iframe element placed in Composr so it has the same height as the actual contents of the frame -- hence eliminating the need for a vertical scrollbar. Note that if you do this, the embedded system must be on the exact same domain as Composr, or web browser security will prevent the height detection from working.

[code="Comcode"]
[html]
<script>
// <![CDATA[
function resizeEmbeddedFrame()
{
	var frame=document.getElementById('frame');
	if ((frame) && (top.frames['frame'].document.body))
	{
		if (top.frames['frame'].document.body.offsetHeight+'px'!=frame.style.height)
		{
			frame.style.height=top.frames['frame'].document.body.offsetHeight+'px';
			frame.scrolling='no';
		}
	} else clearInterval(tid);
}
//]]></script>
</script>

<iframe frameBorder="0" scrolling="no" title="<whatever the embedded system is>" name="frame" id="frame" src="<whatever the system URL is>" style="width: 100%; height: 900px;"><whatever the embedded system is></iframe>

<script>
// <![CDATA[
	var tid=window.setInterval(function() { resizeEmbeddedFrame(); },500);
//]]></script>
[/html]
[/code]

The drawback on relying on this auto-resizing method is that it is somewhat computationally intensive on users' web browsers (doing a check twice a second). It is likely that users will not notice, however. When Composr does its own iframes it actually ties in a bit of code to anything that would change the frame height, to make it resize on demand, rather than routine checking -- but this is not really very easy to do if you are working with someone else's code.

[title="3"]Naturally[/title]

The most preferable method of getting the other system to display inside Composr is to 'port' it to Composr, as a properly constructed module, mini-module, block or mini-block. This would be very possible for most systems, but also a very significant programming effort.

The quickest way is to use mini-modules/mini-blocks. These are simplified equivalents to the normal Composr blocks and modules -- anything echoed out is put into the output stream in the place you'd expect. They are placed in the correspondingly named directories in Composr's file-system, and then you can just use them as normal pages/blocks.

Composr is written so that it treats 'PHP notices' as fatal errors. This is a part of our quality standards. Some poorly written PHP code, however, is designed for PHP configurations that suppress these notices. To work around this we turn off a lot of our standard checking settings when mini-modules or mini-blocks are loaded.

[title="4"]Integrations via direct PHP code[/title]

You may wish to integrate with another database (i.e. not Composr's main database), third-party web-services, or other programming languages.

These complexities can be dealt with in the same way as any PHP programmer would deal with them, while making use of Composr's APIs only as is convenient.

For example, you can use:
 - standard PHP database functionality to open a new connection (MySQL, SQL Server, Oracle, ...)
 - PHP's [tt]file_get_contents[/tt] function to download from REST web services
 - PHP's [tt]file_get_contents[/tt] function to download from scripts written in another language, bridging their output over into the Composr page
 - PHP's [url="SOAP support"]https://php.net/manual/en/book.soap.php[/url] to integrate with more complex REST services (assuming the PHP SOAP extension is installed)
 - PHP's [url="COM support"]https://php.net/manual/en/book.com.php[/url] to integrate to ASP code (assuming the PHP COM extension is installed)

[title="5"]Example[/title]

This simple example shows how to bridge one web system, to another. The example embeds DuckDuckGo (a search engine) into your page. Naturally, this is a silly example, but the same technique can be used to bridge your own scripts, should you be more comfortable programming in a language other than PHP.

[code="PHP"]
<?php

echo '<div style="position: relative; overflow: hidden">';
echo file_get_contents('https://duckduckgo.com/');
echo '</div>';
[/code]

Note I had to add a little CSS to stop DuckDuckGo trying to render parts of its layout on-top of the Composr website.

[title="3"]Third-party APIs (for developers)[/title]

If you are using a third-party API with Composr, you can upload all the PHP files to the [tt]sources_custom[/tt] directory.

Before calling the third-party code it is advisable to call these lines of PHP code:
[code="PHP"]
require_code('developer_tools');
destrictify();
[/code]

These lines will turn off a lot of Composr strictness, and allow include-paths to work better (many PHP files will make assumptions that they are running from inside the include-path).

To load up them use either normal PHP code, or like [tt]require_code('example');[/tt] (for [tt]sources_custom/example.php[/tt]).

[title="3"]Code-based relay[/title]

A compromise between a naturally ported system and a framed system would be to actually write a Composr module that loads the web pages from the other system and puts their output directly into Composr's output stream. Composr has a special API for this, in the 'integrator' source file.

There are two major problems with this approach:
1) The embedded system would always see the server's IP address instead of the clients; this could lead to security issues if it uses IP addresses as a part of its security model
2) It would be ugly (e.g. mixed visual styles), unless extra work was done to clean things up

[title="3"]RSS/Atom[/title]

If you are trying to integrate a system that is non-interactive, and outputs all important information in either the RSS or Atom families of feed formats, you may be able to perform an integration by simply using the [tt]main_rss[/tt] block in combination with the feed URL (or if it is date based information, overlaying the feed URL onto the calendar).
This form of integration is most appropriate for news and calendars, although other forms of information do sometimes fit it well.

You can also export Composr data using RSS.

Use of RSS is documented in the [page="_SEARCH:tut_adv_news"]Advanced News[/page] tutorial.

[title="2"]Integrating JavaScript libraries into Composr[/title]

There are a number of different techniques you can use.

[title="3"]A direct reference by URL[/title]

If you are simply themeing your own site then the simplest thing to do is to just insert a [tt]<script>[/tt] element to the [tt]HTML_HEAD.tpl[/tt] template or the [tt]GLOBAL_HTML_WRAP.tpl[/tt] template (near the footer).

JavaScript libraries usually advise following a process like this because it's pretty standard. They will usually tell you whether to place in the header or the footer. Things go in the header when they are needed for the main HTML to be viable, while things that add to the page (i.e. can be loaded secondarily) go near the footer.

You may choose to reference the JavaScript library as a remote URL. Using a CDN (e.g. https://cdnjs.com/) improves performance because the visitor may well already have the JavaScript library cached in their browser.

You may also host the JavaScript library as local URLs. By convention we put them in [tt]uploads/website_specific/[/tt], but you may put them somewhere else if you prefer.

[title="3"]Copy & pasting into the global JavaScript code[/title]

There's a special process for this:
1) Go to override the [tt]global.js[/tt] template in your theme
2) Blank out your overridden version
3) Put [tt]/*{+START,INCLUDE,global,.js,javascript}{+END}*/[/tt] at the top
4) Paste all the JavaScript next

This works by including the default code in, then your code follows.

[title="3"]Adding formally to the Composr theme system[/title]

If you are a programmer writing an addon, you should add the JavaScript as a proper theme resource, and then do an include command to include it.
This way, the JavaScript code can be overridden within themes, optimised by Composr, and generally will be consistent with how Composr's native JavaScript is handled.

We'll assume the JavaScript file is [tt]example.js[/tt].

Copy the [tt]example.js[/tt] file to [tt]themes/default/javascript_custom/example.js[/tt].
If there is both a minified (compressed) and non-minified version, use the non-minified version. Composr does its own minification automatically, and it's better you be able to read the JavaScript code properly. Additional this way is it less likely the Tempcode parser will accidentally try and interpret parts of the code as Tempcode constructs.

You may wish to edit the file to include this at the top:
[code="CSS"]
/*{$,parser hint: pure}*/
[/code]
This code will turn off Tempcode parsing ("pure"), so that bits of JavaScript that look like Tempcode will not be treated as such.

If you are doing a really tidy job you'll want to leave Tempcode parsing on, and ensure the file does parse as Tempcode (maybe you need to add some slashes or spaces to make that work right).
An advantage of supporting Tempcode is then you can use language string and theme image references within the file, so that it works seamlessly to Composr's standard architecture.

If you want to include the JavaScript library from PHP code, do:
[code="PHP"]
require_javascript('example');
[/code]

If you want to include the JavaScript library from a template, do:
[code="Tempcode"]
{$REQUIRE_JAVASCRIPT,example}
[/code]

[title="4"]With Tempcode parsing[/title]

As mentioned above, you can use Tempcode so that text and images go through the normal Composr architecture.

You may find you need to put some "\" symbols before some "{" symbols, to stop certain bits being parsed as Tempcode.
For example, if the file contained [tt]{Example}[/tt], you'd need to change it to [tt]\{Example}[/tt] because to Composr this looks like a template parameter.

Let's say that [tt]example.js[/tt] contains:
[code="JavaScript"]
var ex_alert_string='An error has happened.';
[/code]
We could either try and match this to an existing Composr language string, or make a new one. Let's make a new one.
We create [tt]lang_custom/EN/example.ini[/tt]:
[code="INI"]
[strings]
EX_ALERT_STRING=An error has happened.
[/code]
And then change the code in [tt]example.js[/tt] to:
[code="JavaScript"]
var ex_alert_string='{example:EX_ALERT_STRING;}';
[/code]

Let's say that [tt]example.js[/tt] also contains:
[code="JavaScript"]
var ex_alert_image='images/alert.png';
[/code]
We could copy the [tt]images[/tt] directory from the JavaScript library as [tt]themes/default/images_custom/example[/tt], and then change the code in [tt]example.js[/tt] to:
[code="JavaScript"]
var ex_alert_image='{$IMG;,example/alert}';
[/code]

As you can see, we are now managing the resources for the JavaScript library with full internationalisation and theme image support!

[title="3"]Ad-hoc code inclusion from PHP code[/title]

If you're working on an addon but for some reason do not want to formally add JavaScript to the Composr theme system, you will want to code it into your addon to insert the necessary code into the header and footer dynamically (because overriding core templates in an addon ties the end-user down too much).
You can do this by calling [tt]attach_to_screen_header[/tt] with whatever HTML is required. Typical code will look like:
[code="PHP"]
attach_to_screen_header(make_string_tempcode('Extra code goes here'));
[/code]

You could inject a JavaScript element that includes an external URL, or a JavaScript element including actual JavaScript code. There are no constraints, it simply adds whatever you specify to the predetermined spots in the head/footer.

There is an equivalent [tt]attach_to_screen_footer[/tt] function.

[title="2"]Integrating CSS libraries into Composr[/title]

There are a number of different techniques you can use.

[title="3"]A direct reference by URL[/title]

If you are simply themeing your own site then the simplest thing to do is to just insert a [tt]<link>[/tt] element to the [tt]HTML_HEAD.tpl[/tt] template.

By convention we put CSS files in [tt]uploads/website_specific/[/tt], but you may put them somewhere else if you prefer.

[title="3"]Copy & pasting into the global CSS[/title]

You may edit the [tt]global.css[/tt] file for your theme and paste all the CSS in there.
Only do this if you are making your own theme or your own site, it's not appropriate for an addon that you share with others.

[title="3"]Adding formally to the Composr theme system[/title]

If you are a programmer writing an addon, you should add the CSS as a proper theme resource, and then do an include command to include it.
This way, the CSS can be overridden within themes, optimised by Composr, and generally will be consistent with how Composr's native CSS is handled.

We'll assume the CSS file is [tt]example.css[/tt].

Copy the [tt]example.css[/tt] file to [tt]themes/default/css_custom/example.css[/tt].
If there is both a minified (compressed) and non-minified version, use the non-minified version. Composr does its own minification automatically, and it's better you be able to read the CSS code properly.

If you want to include the CSS library from PHP code, do:
[code="PHP"]
require_css('example');
[/code]

If you want to include the CSS library from a template, do:
[code="Tempcode"]
{$REQUIRE_CSS,example}
[/code]

[title="2"]Look and feel when integrating into Composr[/title]

The other system is unlikely to naturally fit in with your Composr theme: therefore you may need to make an effort to make them look alike. This would either involve changing Composr, changing the other system, or changing both to coalesce visually.

It is important to understand that it is impossible for two web systems to 'share' a theme, as themes are designed to theme specific content structures. It is so unlikely as to be essentially impossible by chance, for two different systems to have compatible layout structure.

[title="2"]Sharing members[/title]

It can be a bit tricky to share members between Composr and another system. There are many ways to go about it:
1) Make/have-made a Composr forum driver that is for the other system (as Composr can support different member systems through a forum driver). This is only appropriate if either the other system actually is the forum you will be using, or you are not actually wanting a forum for your site
2) Reprogram the other system so that it uses the same member system that Composr uses (be that Composr's own, or that of a third party system); you might be able to find a modification for the other system that does it already (for instance, if you are using a widespread forum like phpBB, and want to integrate an external system, it is possible that the other system already has a modification to allow it to tie itself to phpBB)
3) Disable joining of members in the other system, and make it so that login checks against whatever member system Composr is using; maintain a separate member database in the other system, but effectively tie it in to whatever Composr is using
4) Use LDAP or HTTP-auth for all systems, with each setting up their own separate 'extra' member information scheme; Composr supports LDAP and HTTP-auth, but systems that do are in the minority so it may be a lot of work bringing other involved systems 'up to speed'
5) Manually synchronise via spreadsheet imports from one system to another
6) Use the [tt]user_sync[/tt] non-bundled addon to programmatically import from one database to another (requires some limited PHP programming to customise it)
7) Use the [tt]user_simple_spreadsheet_sync[/tt] non-bundled addon to tie together spreadsheet import/export between Composr and another system (requires some limited PHP programming to customise it)

These are all very technical tasks, so we do not expect that many users will have the experience to easily carry them out themselves.

[title="3"]Directing external systems to log in a user to a Composr site[/title]

Composr handles log in anywhere as a part of the framework (i.e. any request Composr is handling).
It triggers if these post parameters are present:
 - [tt]username[/tt] (required)
 - [tt]password[/tt] (required)
 - [tt]remember[/tt] (optional, set to '1' if cookies should be saved)
 - [tt]login_invisible[/tt] (optional, set to '1' if the login should be invisible)

(the [tt]login[/tt] page in Composr is mostly a front-end, and doesn't handle log in itself)

If posting from a different domain name then the 'Form-posting partner sites' option must be set to include the domain name you're using.

For completeness, this is what the [tt]login[/tt] page [i]does[/i] do:
 - Shows error messages if a log in fails
 - Performs after-login redirects
 - Handles log outs
 - Performs after-logout redirects
 - Handles Conceded mode
 - Handles "Become invisible" toggle

If coding a system to create a Composr login automatically (i.e. without needing to direct the user to a POST request) you'd need to set the session cookie to a random number, and then insert a row into the Composr [tt]sessions[/tt] table to match against it.

[title="2"]Creating new forms in HTML but not PHP[/title]

You may need to tune the "Pages without security tokens" configuration option (Admin Zone > Setup > Configuration > Security options > Advanced) if you are setting up pages with custom forms but not going through the full Composr forms API.

[title="2"]General integration for external systems to access Composr[/title]

As Composr is Open Source, you can add whatever integration code you wish to it so that other systems have a way in.

However, the framework for this has been done for you with the endpoints API, and [page="_SEARCH:tut_mobile_sdk"]Composr Mobile SDK[/page] provides a ready-made set of endpoints covering many common use cases.
New endpoint hooks can be created by a PHP programmer easily, providing whatever custom REST API integration is required.

[concepts
 1_key="iframe"       1_value="A portion of the main web page that encloses another web page (with its own URL)"
 2_key="Mini-block"   2_value="A PHP script that works as a Composr block, with full access to Composr's API but not having to follow any particular structure (to output just [tt]echo[/tt] or [tt]return[/tt]); the page equivalent of a mini-block is a mini-module"
]Concepts[/concepts]

[title="2"]See also[/title]

 - [page="_SEARCH:tut_mobile_sdk"]Mobile apps via Composr Mobile SDK[/page]
 - [page="_SEARCH:tut_nuances"]Nuances of forum integration[/page]
 - [page="_SEARCH:tut_structure"]Composr site structure[/page]
 - [page="_SEARCH:tut_httpauth"]Integrating Composr into a network via HTTP authentication[/page]
 - [page="_SEARCH:tut_ldap"]Integrating Composr into a corporate network via LDAP[/page]

{$SET,tutorial_tags,Third Party Integration,syndication,expert}{$SET,tutorial_add_date,Aug 2008}{$SET,tutorial_summary,This tutorial will provide details on how to integrate Composr with another web system installed for your website.}[block]main_tutorial_rating[/block]
