Review, cleanup and refresh of integration section

This commit is contained in:
Will Rossiter 2014-09-28 16:59:48 +13:00 committed by Cam Findlay
parent 0c73768e8c
commit 3d8c9232e0
9 changed files with 469 additions and 235 deletions

View File

@ -1,49 +1,66 @@
summary: Consume external data through their RESTFul interfaces.
# Restful Service # Restful Service
## Introduction `[api:RestfulService]` is used to enable connections to remote web services through PHP's `curl` command. It provides an
interface and utility functions for generating a valid request and parsing the response returned from the web service.
`[api:RestfulService]` uses the php curl library, enabling connections to remote web services which support a REST interface and consuming those web services. (Examples: [Flickr](http://www.flickr.com/services/api/), [Youtube](http://code.google.com/apis/youtube/overview.html), Amazon and etc). `[api:RestfulService]` can parse the XML response (sorry no JSON support)
returned from the web service. Further it supports caching of the response, and you can customize the cache interval.
To gain the functionality you can either create a new `[api:RestfulService]` object or create a class extending the
RestfulService (see [flickrservice](http://silverstripe.org/flickr-module/) and
[youtubeservice](http://silverstripe.org/youtube-gallery-module/) modules).
<div class="alert" markdown="1">
RestfulService currently only supports XML. It has no JSON support at this stage.
</div>
## Examples ## Examples
### Creating a new RestfulObject ### Creating a new RestfulObject
:::php `getWellingtonWeather` queries the Yahoo Weather API for an XML file of the latest weather information. We pass a query
//example for using RestfulService to connect and retrive latest twitter status of an user. string parameter `q` with the search query and then convert the resulting XML data to an `ArrayList` object to display
$twitter = new RestfulService("http://twitter.com/statuses/user_timeline/user.xml", $cache_expiry ); in the template.
$params = array('count' => 1);
$twitter->setQueryString($params);
$conn = $twitter->request();
$msgs = $twitter->getValues($conn, "status");
**mysite/code/Page.php**
### Extending to a new class
:::php :::php
//example for extending RestfulService public function getWellingtonWeather() {
class FlickrService extends RestfulService { $fetch = new RestfulService(
'https://query.yahooapis.com/v1/public/yql'
);
public function __construct($expiry=NULL){ $fetch->setQueryString(array(
parent::__construct('http://www.flickr.com/services/rest/', $expiry); 'q' => 'select * from weather.forecast where woeid in (select woeid from geo.places(1) where text="Wellington, NZ")'
$this->checkErrors = true; ));
// perform the query
$conn = $fetch->request();
// parse the XML body
$msgs = $fetch->getValues($conn->getBody(), "results");
// generate an object our templates can read
$output = new ArrayList();
if($msgs) {
foreach($msgs as $msg) {
$output->push(new ArrayData(array(
'Description' => Convert::xml2raw($msg->channel_item_description)
)));
}
} }
......
return $output;
}
### Multiple requests by using the $subURL argument on connect() ## Features
### Basic Authenication
:::php :::php
// Set up REST service
$service = new RestfulService("http://example.harvestapp.com"); $service = new RestfulService("http://example.harvestapp.com");
$service->basicAuth('username', 'password'); $service->basicAuth('username', 'password');
$service->httpHeader('Accept: application/xml');
$service->httpHeader('Content-Type: application/xml'); ### Make multiple requests
:::php
$service = new RestfulService("http://example.harvestapp.com");
$peopleXML = $service->request('/people'); $peopleXML = $service->request('/people');
$people = $service->getValues($peopleXML, 'user'); $people = $service->getValues($peopleXML, 'user');
@ -54,21 +71,20 @@ RestfulService (see [flickrservice](http://silverstripe.org/flickr-module/) and
$tasks = $service->getValues($taskXML, 'task'); $tasks = $service->getValues($taskXML, 'task');
## Features
### Caching ### Caching
To set the cache interval you can pass it as the 2nd argument to constructor. To set the cache interval you can pass it as the 2nd argument to constructor.
:::php :::php
new RestfulService("http://twitter.com/statuses/user_timeline/user.xml", 3600 ); $expiry = 60 * 60; // 1 hour;
$request = new RestfulService("http://example.harvestapp.com", $expiry );
### Getting Values & Attributes ### Getting Values & Attributes
You can traverse throught document tree to get the values or attribute of a particular node. You can traverse through document tree to get the values or attribute of a particular node using XPath. Take for example
for example you can traverse the following XML that is returned.
:::xml :::xml
<entries> <entries>
@ -78,62 +94,77 @@ for example you can traverse
<entry id='22'>John</entry> <entry id='22'>John</entry>
</entries> </entries>
to extract the id attributes of the entries use: To extract the id attributes of the entries use:
:::php :::php
$this->getAttributes($xml, "entries", "entry") //will return all attributes of each entry node $this->getAttributes($xml, "entries", "entry");
// array(array('id' => 12), array('id' => '15'), ..)
to extract the values (the names) of the entries use: To extract the values (the names) of the entries use:
:::php :::php
$this->getValues($xml, "entries", "entry") //will return all values of each entry node $this->getValues($xml, "entries", "entry");
// array('Sally', 'Ted', 'Matt', 'John')
### Searching for Values & Attributes ### Searching for Values & Attributes
If you don't know the exact position of dom tree where the node will appear you can use xpath to search for the If you don't know the exact position of DOM tree where the node will appear you can use xpath to search for the node.
node.Recommended for retrieving values of namespaced nodes.
<div class="note">
This is the recommended method for retrieving values of name spaced nodes.
</div>
:::xml :::xml
<media:guide> <media:guide>
<media:entry id="2030">video</media:entry> <media:entry id="2030">video</media:entry>
</media:guide> </media:guide>
to get the value of entry node with the namespace media, use: To get the value of entry node with the namespace media, use:
:::php :::php
$this->searchValue($response, "//media:guide/media:entry") $this->searchValue($response, "//media:guide/media:entry");
// array('video');
## Best Practices ## Best Practices
### Handling Errors ### Handling Errors
If the web service returned an error (for example, API key not available or inadequate parameters) `[api:RestfulService]` If the web service returned an error (for example, API key not available or inadequate parameters),
could delgate the error handling to it's descendant class. To handle the errors define a function called errorCatch `[api:RestfulService]` can delegate the error handling to it's descendant class. To handle the errors, subclass
`RestfulService and define a function called errorCatch.
:::php :::php
// This will raise Youtube API specific error messages (if any). <?php
class MyRestfulService extends RestfulService {
public function errorCatch($response) { public function errorCatch($response) {
$err_msg = $response; $err_msg = $response;
if(strpos($err_msg, '<') === false) { if(strpos($err_msg, '<') === false) {
user_error("YouTube Service Error : $err_msg", E_USER_ERROR); user_error("YouTube Service Error : $err_msg", E_USER_ERROR);
} }
return $response; return $response;
} }
}
If you want to bypass error handling, define `checkErrors` in the constructor for `RestfulService`
If you want to bypass error handling on your sub-classes you could define that in the constructor.
:::php :::php
<?php
class MyRestfulService extends RestfulService {
public function __construct($expiry = NULL) { public function __construct($expiry = NULL) {
parent::__construct('http://www.flickr.com/services/rest/', $expiry); parent::__construct('http://www.flickr.com/services/rest/', $expiry);
$this->checkErrors = false; //Set checkErrors to false to bypass error checking
$this->checkErrors = false;
}
} }
@ -182,41 +213,11 @@ $service->request('service.json', 'GET', null, null, $curlOptions);
``` ```
## Other Uses
### How to use `[api:RestfulService]` to easily embed an RSS feed ## How to's
`[api:RestfulService]` can be used to easily embed an RSS feed (since it's also an xml response) from a site
such as del.icio.us
Put something like this code in mysite/code/Page.php inside class Page_Controller
:::php
// Accepts an RSS feed URL and outputs a list of links from it
public function RestfulLinks($url){
$service = new RestfulService($url);
$request = $service->request();
$body = $request->getBody();
$items = $service->getValues($body,"channel","item");
$output = '';
foreach($items as $item) {
// Fix quote encoding
$description = str_replace('&amp;quot;', '&quot;', $item->description);
$output .= "<li><a href=\"{$item->link}\">{$item->title}</a><br />{$description}</li>";
}
return $output;
}
Put something like this code in `themes/<your-theme>/templates/Layout/HomePage.ss`:
:::ss
<h3>My Latest Del.icio.us Links</h3>
<ul>
$RestfulLinks(http://del.icio.us/rss/elijahlofgren)
</ul>
* [Embed an RSS Feed](how_to/embed_rss)
## API Documentation ## API Documentation
`[api:RestfulService]`
* `[api:RestfulService]`

View File

@ -1,27 +1,26 @@
title: RSS Feed
summary: Output records from your database as an RSS Feed.
# RSS Feed # RSS Feed
## Introduction Generating RSS / Atom-feeds is a matter of rendering a `[api:SS_List]` instance through the `[api:RSSFeed]` class.
Generating RSS/Atom-feeds is a matter of rendering a `[api:SS_List]` through The `[api:RSSFeed]` class doesn't limit you to generating article based feeds, it is just as easy to create a feed of
the `[api:RSSFeed]` class. your current staff members, comments or any other custom `[api:DataObject]` subclasses you have defined. The only
logical limitation here is that every item in the RSS-feed should be accessible through a URL on your website, so it's
advisable to just create feeds from subclasses of `[api:SiteTree]`.
The `[api:RSSFeed]` class doesn't limit you to generating article based feeds, <div class="warning" markdown="1">
it is just as easy to create a feed of your current staff members, comments or If you wish to generate an RSS feed that contains a `[api:DataObject]`, ensure you define a `AbsoluteLink` method on
any other custom `[api:DataObject]` subclasses you have defined. The only the object.
logical limitation here is that every item in the RSS-feed should be accessible </div>
through a URL on your website, so its advisable to just create feeds from sub
classes of `[api:SiteTree]`.
If you wish to generate an RSS feed for `[api:DataObject]` instances, ensure they
define an AbsoluteLink() method.
## Usage ## Usage
:::php Including an RSS feed has two steps. First, a `Controller` action which responses with the `XML` and secondly, the other
RSSFeed::linkToFeed($link, $title) web pages need to link to the URL to notify users that the RSS feed is available and where it is.
This line should go in your `[api:Controller]` subclass in the action you want An outline of step one looks like:
to include the HTML link. Not all arguments are required, see `[api:RSSFeed]` and example below. Last Modified Time is expected in seconds like time().
:::php :::php
$feed = new RSSFeed( $feed = new RSSFeed(
@ -36,63 +35,48 @@ to include the HTML link. Not all arguments are required, see `[api:RSSFeed]` an
$etag $etag
); );
Creates a new `[api:RSSFeed]` instance to be returned. The arguments notify $feed->outputToBrowser();
SilverStripe what values to include in the feed.
To achieve step two include the following code where ever you want to include the `<link>` tag to the RSS Feed. This
will normally go in your `Controllers` `init` method.
:::php
RSSFeed::linkToFeed($link, $title);
## Examples ## Examples
### Showing latest blog posts
:::php
class Page_Controller extends ContentController {
private static $allowed_actions = array('rss');
public function init() {
parent::init();
// linkToFeed will add an appropriate HTML link tag to the website
// <head> tag to notify web browsers that an RSS feed is available
// for this page. You can include as many feeds on the page as you
// wish as long as each as a different link. For example:
// ('blog/rss', 'staff/rss').
//
// In this example $this->Link("rss") refers to the *rss* function
// we define below.
RSSFeed::linkToFeed($this->Link("rss"), "RSS feed of this blog");
}
public function rss() {
// Creates a new RSS Feed list
$rss = new RSSFeed(
$list = $this->getBlogPosts(), // an SS_List containing your feed items
$link = $this->Link("rss"), // a HTTP link to this feed
$title = "My feed", // title for this feed, displayed in RSS readers
$description = "This is an example feed." // description
);
// Outputs the RSS feed to the user.
return $rss->outputToBrowser();
}
public function getBlogPosts() {
return BlogPage::get()->limit(10);
}
}
### Showing the 10 most recently updated pages ### Showing the 10 most recently updated pages
You can use `[api:RSSFeed]` to easily create a feed showing your latest Page You can use `[api:RSSFeed]` to easily create a feed showing your latest Page updates. The following example adds a page
updates. Update mysite/code/Page.php to something like this: `/home/rss/` which displays an XML file the latest updated pages.
**mysite/code/Page.php**
:::php :::php
<?php <?php
class Page extends SiteTree {}
..
class Page_Controller extends ContentController { class Page_Controller extends ContentController {
private static $allowed_actions = array('rss'); private static $allowed_actions = array(
'rss'
);
public function init() { public function init() {
parent::init(); parent::init();
RSSFeed::linkToFeed($this->Link() . "rss", "10 Most Recently Updated Pages"); RSSFeed::linkToFeed($this->Link() . "rss", "10 Most Recently Updated Pages");
} }
public function rss() { public function rss() {
$rss = new RSSFeed($this->LatestUpdates(), $this->Link(), "10 Most Recently Updated Pages", "Shows a list of the 10 most recently updated pages."); $rss = new RSSFeed(
$this->LatestUpdates(),
$this->Link(),
"10 Most Recently Updated Pages",
"Shows a list of the 10 most recently updated pages."
);
return $rss->outputToBrowser(); return $rss->outputToBrowser();
} }
@ -103,78 +87,109 @@ updates. Update mysite/code/Page.php to something like this:
### Rendering DataObjects in a RSSFeed ### Rendering DataObjects in a RSSFeed
DataObjects can be rendered in the feed as well, however, since they aren't explicitly DataObjects can be rendered in the feed as well, however, since they aren't explicitly `[api:SiteTree]` subclasses we
`[api:SiteTree]` subclasses we need to include a function `AbsoluteLink` to allow the need to include a function `AbsoluteLink` to allow the RSS feed to link through to the item.
RSS feed to link through to the item.
If the items are all displayed on a single page you may simply hard code the link to <div class="info">
point to a particular page. If the items are all displayed on a single page you may simply hard code the link to point to a particular page.
</div>
Take an example, we want to create an RSS feed of all the Students, a DataObject we Take an example, we want to create an RSS feed of all the `Players` objects in our site. We make sure the `AbsoluteLink`
defined in the [fifth tutorial](/tutorials/5-dataobject-relationship-management). method is defined and returns a string to the full website URL.
:::php :::php
<?php <?php
class Student extends DataObject {
class Player extends DataObject {
public function AbsoluteLink() { public function AbsoluteLink() {
// see tutorial 5, students are assigned a project, so the 'link' // assumes players can be accessed at yoursite.com/players/2
// to view the student is based on their projects link.
return $this->Project()->AbsoluteLink(); return Controller::join_links(
Director::absoluteBaseUrl(),
'players',
$this->ID
);
} }
} }
Then update the Page_Controller class in mysite/code/Page.php to include an RSSFeed Then in our controller, we add a new action which returns a the XML list of `Players`.
for all the students as we've seen before.
:::php :::php
<?php
class Page_Controller extends ContentController { class Page_Controller extends ContentController {
private static $allowed_actions = array('students');
private static $allowed_actions = array(
'players'
);
public function init() { public function init() {
parent::init(); parent::init();
RSSFeed::linkToFeed($this->Link("students"), "Students feed");
RSSFeed::linkToFeed($this->Link("players"), "Players");
} }
public function students() {
public function players() {
$rss = new RSSFeed( $rss = new RSSFeed(
$list = $this->getStudents(), Player::get(),
$link = $this->Link("students"), $this->Link("players"),
$title = "Students feed" "Players"
); );
return $rss->outputToBrowser(); return $rss->outputToBrowser();
} }
public function getStudents() {
return Student::get()->sort("Created", "DESC")->limit(10);
}
} }
### Customizing the RSS Feed template ### Customizing the RSS Feed template
The default template used is framework/templates/RSSFeed.ss and includes The default template used for XML view is `framework/templates/RSSFeed.ss`. This template displays titles and links to
displaying titles and links to the content. If you have a particular need the object. To customize the XML produced use `setTemplate`.
for customizing the XML produced (say for additional meta data) use `setTemplate`.
Taking that last example, we would rewrite the students function to include a Say from that last example we want to include the Players Team in the XML feed we might create the following XML file.
unique template (write your own XML in themes/yourtheme/templates/Students.ss)
**mysite/templates/PlayersRss.ss**
:::xml
<?xml version="1.0"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>$Title</title>
<link>$Link</link>
<atom:link href="$Link" rel="self" type="application/rss+xml" />
<description>$Description.XML</description>
<% loop $Entries %>
<item>
<title>$Title.XML</title>
<team>$Team.Title</team>
</item>
<% end_loop %>
</channel>
</rss>
`setTemplate` can then be used to tell RSSFeed to use that new template.
**mysite/code/Page.php**
:::php :::php
public function students() {
public function players() {
$rss = new RSSFeed( $rss = new RSSFeed(
$list = $this->getStudents(), Player::get(),
$link = $this->Link("students"), $this->Link("players"),
$title = "Students feed" "Players"
); );
$rss->setTemplate('Students');
$rss->setTemplate('PlayersRss');
return $rss->outputToBrowser(); return $rss->outputToBrowser();
} }
## External Sources <div class="warning">
As we've added a new template (PlayersRss.ss) make sure you clear your SilverStripe cache.
</div>
`[api:RSSFeed]` only creates feeds from your own data. We've included the [SimplePie](http://simplepie.org) RSS-parser for
accessing feeds from external sources.
## Related
* [blog module](http://silverstripe.org/blog-module)
## API Documentation ## API Documentation

View File

@ -0,0 +1,69 @@
title: Import CSV Data through a Controller
# Import CSV Data through a Controller
You can have more customized logic and interface feedback through a custom controller. Let's create a simple upload
form (which is used for `MyDataObject` instances). You can access it through
`http://yoursite.com/MyController/?flush=all`.
:::php
<?php
class MyController extends Controller {
private static $allowed_actions = array(
'Form'
);
protected $template = "BlankPage";
public function Link($action = null) {
return Controller::join_links('MyController', $action);
}
public function Form() {
$form = new Form(
$this,
'Form',
new FieldList(
new FileField('CsvFile', false)
),
new FieldList(
new FormAction('doUpload', 'Upload')
),
new RequiredFields()
);
return $form;
}
public function doUpload($data, $form) {
$loader = new CsvBulkLoader('MyDataObject');
$results = $loader->load($_FILES['CsvFile']['tmp_name']);
$messages = array();
if($results->CreatedCount()) {
$messages[] = sprintf('Imported %d items', $results->CreatedCount());
}
if($results->UpdatedCount()) {
$messages[] = sprintf('Updated %d items', $results->UpdatedCount());
}
if($results->DeletedCount()) {
$messages[] = sprintf('Deleted %d items', $results->DeletedCount());
}
if(!$messages) {
$messages[] = 'No changes';
}
$form->sessionMessage(implode(', ', $messages), 'good');
return $this->redirectBack();
}
}
<div class="alert" markdown="1">
This interface is not secured, consider using [api:Permission::check()] to limit the controller to users with certain
access rights.
</div>

View File

@ -0,0 +1,103 @@
title: A custom CSVBulkLoader instance
# How to: A custom CSVBulkLoader instance
A an implementation of a custom `CSVBulkLoader` loader. In this example. we're provided with a unique CSV file
containing a list of football players and the team they play for. The file we have is in the format like below.
"SpielerNummer", "Name", "Geburtsdatum", "Gruppe"
11, "John Doe", 1982-05-12,"FC Bayern"
12, "Jane Johnson", 1982-05-12,"FC Bayern"
13, "Jimmy Dole",,"Schalke 04"
This data needs to be imported into our application. For this, we have two `DataObjects` setup. `Player` contains
information about the individual player and a relation set up for managing the `Team`.
**mysite/code/Player.php**.
:::php
<?php
class Player extends DataObject {
private static $db = array(
'PlayerNumber' => 'Int',
'FirstName' => 'Text',
'LastName' => 'Text',
'Birthday' => 'Date'
);
private static $has_one = array(
'Team' => 'FootballTeam'
);
}
**mysite/code/FootballTeam.php**
:::php
<?php
class FootballTeam extends DataObject {
private static $db = array(
'Title' => 'Text'
);
private static $has_many = array(
'Players' => 'Player'
);
}
Now going back to look at the CSV, we can see that what we're provided with does not match what our data model looks
like, so we have to create a sub class of `CsvBulkLoader` to handle the unique file. Things we need to consider with
the custom importer are:
* Convert property names (e.g Number to PlayerNumber) through providing a `$columnMap`.
* Split a combined "Name" field into `FirstName` and `LastName` by calling `importFirstAndLastName` on the `Name`
column
* Prevent duplicate imports by a custom `$duplicateChecks` definition.
* Create a `Team` automatically based on the `Gruppe` column and a entry for `$relationCallbacks`
Our final import looks like this.
**mysite/code/PlayerCsvBulkLoader.php**
:::php
<?php
class PlayerCsvBulkLoader extends CsvBulkLoader {
public $columnMap = array(
'Number' => 'PlayerNumber',
'Name' => '->importFirstAndLastName',
'Geburtsdatum' => 'Birthday',
'Gruppe' => 'Team.Title',
);
public $duplicateChecks = array(
'SpielerNummer' => 'PlayerNumber'
);
public $relationCallbacks = array(
'Team.Title' => array(
'relationname' => 'Team',
'callback' => 'getTeamByTitle'
)
);
public static function importFirstAndLastName(&$obj, $val, $record) {
$parts = explode(' ', $val);
if(count($parts) != 2) return false;
$obj->FirstName = $parts[0];
$obj->LastName = $parts[1];
}
public static function getTeamByTitle(&$obj, $val, $record) {
return FootballTeam::get()->filter('Title', $val)->First();
}
}
## Related
* [api:CsvParser]
* [api:ModelAdmin]

View File

@ -0,0 +1,57 @@
title: Embed an RSS Feed
# Embed an RSS Feed
`[api:RestfulService]` can be used to easily embed an RSS feed from a site. In this How to we'll embed the latest
weather information from the Yahoo Weather API.
First, we write the code to query the API feed.
**mysite/code/Page.php**
:::php
public function getWellingtonWeather() {
$fetch = new RestfulService(
'https://query.yahooapis.com/v1/public/yql'
);
$fetch->setQueryString(array(
'q' => 'select * from weather.forecast where woeid in (select woeid from geo.places(1) where text="Wellington, NZ")'
));
// perform the query
$conn = $fetch->request();
// parse the XML body
$msgs = $fetch->getValues($conn->getBody(), "results");
// generate an object our templates can read
$output = new ArrayList();
if($msgs) {
foreach($msgs as $msg) {
$output->push(new ArrayData(array(
'Description' => Convert::xml2raw($msg->channel_item_description)
)));
}
}
return $output;
}
This will provide our `Page` template with a new `WellingtonWeather` variable (an [api:ArrayList]). Each item has a
single field `Description`.
**mysite/templates/Page.ss**
:::ss
<% if WellingtonWeather %>
<% loop WellingtonWeather %>
$Description
<% end_loop %>
<% end_if %>
## Related
* [RestfulService Documentation](../restfulservice)
* `[api:RestfulService]`

View File

@ -1,7 +1,5 @@
summary: Integrate other web services within your application or make your SilverStripe data available via REST. summary: Integrate other web services within your application or make your SilverStripe data available.
introduction: Integrate other web services within your application or make your SilverStripe data available.
title: Integration and Web Services
[CHILDREN] [CHILDREN]
## How-to
[CHILDREN How_To]

View File

@ -5,20 +5,20 @@ summary: Provide your users with advanced search functionality.
## Searching for Pages (and Files) ## Searching for Pages (and Files)
Fulltext search for page content (and other attributes like "Title" or "MetaTags") can be easily added to SilverStripe. Fulltext search for page content (and other attributes like "Title" or "MetaTags") can be easily added to SilverStripe.
See [Tutorial: Site Search](/tutorials/4-site-search) for details. See [Tutorial: Site Search](/tutorials/site_search) for details.
## Searching for DataObjects ## Searching for DataObjects
The `[api:SearchContext]` class provides a good base implementation that you can hook into your own controllers. The `[api:SearchContext]` class provides a good base implementation that you can hook into your own controllers.
A working implementation of searchable DataObjects can be seen in the `[ModelAdmin](/reference/modeladmin)` class. A working implementation of searchable DataObjects can be seen in the [ModelAdmin](../customising_the_cms/modeladmin) class.
[SearchContext](/reference/searchcontext) goes into more detail about setting up a default search form for `[api:DataObject]`s. [SearchContext](/searchcontext) goes into more detail about setting up a default search form for `[api:DataObject]`s.
### Fulltext search on DataObjects ## Fulltext search on DataObjects
The `[api:MySQLDatabase]` class now defaults to creating tables using the InnoDB storage engine. As Fulltext search in MySQL The `[api:MySQLDatabase]` class now defaults to creating tables using the InnoDB storage engine. As Fulltext search in
requires the MyISAM storage engine, any DataObject you wish to use with Fulltext search must be changed to use MyISAM storage MySQL requires the MyISAM storage engine, any DataObject you wish to use with Fulltext search must be changed to use
engine. MyISAM storage engine.
You can do so by adding this static variable to your class definition: You can do so by adding this static variable to your class definition:
@ -29,9 +29,9 @@ You can do so by adding this static variable to your class definition:
## Searching for Documents ## Searching for Documents
SilverStripe does not have a built-in method to search through file content (e.g. in PDF or DOC format). SilverStripe does not have a built-in method to search through file content (e.g. in PDF or DOC format). You can either
You can either extract any textual file content into the `[File](api:File)->Content` property, or use a extract any textual file content into the `[File](api:File)->Content` property, or use a dedicated search service like
dedicated search service like the [sphinx module](http://silverstripe.org/sphinx-module). the [Full module](http://silverstripe.org/sphinx-module).
## Related ## Related

View File

@ -1,48 +1,41 @@
title: i18n
summary: Display templates and PHP code in different languages based on the preferences of your website users. summary: Display templates and PHP code in different languages based on the preferences of your website users.
# i18n # i18n
## Introduction
The i18n class (short for "internationalization") in SilverStripe enables you to display templates and PHP code in The i18n class (short for "internationalization") in SilverStripe enables you to display templates and PHP code in
different languages based on your global settings and the preferences of your website users. This process is also known different languages based on your global settings and the preferences of your website users. This process is also known
as l10n (short for "localization"). as l10n (short for "localization").
For translating any content managed through the CMS or stored in the database, For translating any content managed through the CMS or stored in the database, please use the
please use the "[translatable](http://github.com/silverstripe/silverstripe-translatable)" module. [translatable](http://github.com/silverstripe/silverstripe-translatable) module.
This page aims to describe the low-level functionality of the i18n-API. It targets developers who: This page aims to describe the low-level functionality of the i18n API. It targets developers who:
* are involved in creating templates in different languages
* want to build their own modules with i18n capabilities
* want to make their PHP-code (e.g. form labels) i18n-ready
Please note that this project scope currently **doesn't include full support for format conversion in dates or
currencies**. Check our [roadmap](http://open.silverstripe.com/roadmap).
* Are involved in creating templates in different languages.
* Want to build their own modules with i18n capabilities.
* Want to make their PHP-code (e.g. form labels) i18n-ready
## Usage ## Usage
### Enabling i18n ### Enabling i18n
The i18n class is enabled by default. The i18n class is enabled by default.
### Setting the locale ### Setting the locale
To set the locale you just need to call `[api:i18n::set_locale()]` passing, as a parameter, the name of the locale that you To set the locale you just need to call `[api:i18n::set_locale()]` passing, as a parameter, the name of the locale that
want to set. you want to set.
:::php :::php
//Example 1: setting the locale // mysite/_config.php
i18n::set_locale('de_DE'); // Setting the locale to German (Germany) i18n::set_locale('de_DE'); // Setting the locale to German (Germany)
i18n::set_locale('ca_AD'); // Setting to Catalan (Andorra) i18n::set_locale('ca_AD'); // Setting to Catalan (Andorra)
Once we set a locale, all the calls to the translator function will return strings according to the set locale value, if Once we set a locale, all the calls to the translator function will return strings according to the set locale value, if
these translations are available. See these translations are available. See [unicode.org](http://unicode.org/cldr/data/diff/supplemental/languages_and_territories.html)
[unicode.org](http://unicode.org/cldr/data/diff/supplemental/languages_and_territories.html) for a complete listing of for a complete listing of available locales.
available locales.
### Getting the locale ### Getting the locale
@ -60,7 +53,7 @@ To let browsers know which language they're displaying a document in, you can de
<html lang="$ContentLocale" xml:lang="$ContentLocale" xmlns="http://www.w3.org/1999/xhtml"> <html lang="$ContentLocale" xml:lang="$ContentLocale" xmlns="http://www.w3.org/1999/xhtml">
Setting the '<html>' attribute is the most commonly used technique. There are other ways to specify content languages Setting the `<html>` attribute is the most commonly used technique. There are other ways to specify content languages
(meta tags, HTTP headers), explained in this [w3.org article](http://www.w3.org/International/tutorials/language-decl/). (meta tags, HTTP headers), explained in this [w3.org article](http://www.w3.org/International/tutorials/language-decl/).
You can also set the [script direction](http://www.w3.org/International/questions/qa-scripts), You can also set the [script direction](http://www.w3.org/International/questions/qa-scripts),
@ -218,9 +211,10 @@ the PHP version of the function.
#### Caching in Template Files with locale switching #### Caching in Template Files with locale switching
When caching a `<% loop %>` or `<% with %>` with `<%t params %>`. It is important to add the Locale to the cache key otherwise it won't pick up locale changes. When caching a `<% loop %>` or `<% with %>` with `<%t params %>`. It is important to add the Locale to the cache key
otherwise it won't pick up locale changes.
::::ss :::ss
<% cached 'MyIdentifier', $CurrentLocale %> <% cached 'MyIdentifier', $CurrentLocale %>
<% loop $Students %> <% loop $Students %>
$Name $Name
@ -229,15 +223,12 @@ When caching a `<% loop %>` or `<% with %>` with `<%t params %>`. It is importan
## Collecting text ## Collecting text
To collect all the text in code and template files we have just to visit: To collect all the text in code and template files we have just to visit: `http://localhost/dev/tasks/i18nTextCollectorTask`
`http://localhost/dev/tasks/i18nTextCollectorTask`
Text collector will then read the files, build the master string table for each module where it finds calls to the Text collector will then read the files, build the master string table for each module where it finds calls to the
underscore function, and tell you about the created files and any possible entity redeclaration. underscore function, and tell you about the created files and any possible entity redeclaration.
If you want to run the text collector for just one module you can use the 'module' parameter: If you want to run the text collector for just one module you can use the 'module' parameter:
`http://localhost/dev/tasks/i18nTextCollectorTask/?module=cms` `http://localhost/dev/tasks/i18nTextCollectorTask/?module=cms`
<div class="hint" markdown='1'> <div class="hint" markdown='1'>

View File

@ -21,19 +21,19 @@ All files, images and folders in the 'assets' directory are stored in the databa
If you have the CMS module installed, you can manage files, folders and images in the "Files" section of the CMS. Inside this section, you will see a list of files and folders like below: If you have the CMS module installed, you can manage files, folders and images in the "Files" section of the CMS. Inside this section, you will see a list of files and folders like below:
![](_images/assets.png) ![](/_images/assets.png)
You can click on any file to edit it, or click on any folder to open it. To delete a file or a folder, simply click the red 'X' symbol next to it. If you click to open a folder, you can go back up one level by clicking the 'up' arrow above the folder name (highlighted below): You can click on any file to edit it, or click on any folder to open it. To delete a file or a folder, simply click the red 'X' symbol next to it. If you click to open a folder, you can go back up one level by clicking the 'up' arrow above the folder name (highlighted below):
![](_images/assets_up.png) ![](/_images/assets_up.png)
Once you click to edit a file, you will see a form similar to the one below, in which you can edit the file's title, filename, owner, or even change which folder the file is located in: Once you click to edit a file, you will see a form similar to the one below, in which you can edit the file's title, filename, owner, or even change which folder the file is located in:
![](_images/assets_editform.png) ![](/_images/assets_editform.png)
You may also notice the 'Sync files' button (highlighted below). This button allows CMS users to 'synchronise' the database (remember, all files/folders are stored as database records) with the filesystem. This is particularly useful if someone has uploaded or removed files/folders via FTP, for example. You may also notice the 'Sync files' button (highlighted below). This button allows CMS users to 'synchronise' the database (remember, all files/folders are stored as database records) with the filesystem. This is particularly useful if someone has uploaded or removed files/folders via FTP, for example.
![](_images/assets_sync.png) ![](/_images/assets_sync.png)
## Upload ## Upload