mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
Review, cleanup and refresh of integration section
This commit is contained in:
parent
0c73768e8c
commit
3d8c9232e0
@ -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('&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]`
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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>
|
@ -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]
|
@ -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]`
|
@ -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]
|
|
@ -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
|
||||||
|
|
||||||
|
@ -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'>
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user