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
397843a49f
commit
a08bd9c12f
@ -1,49 +1,66 @@
|
||||
summary: Consume external data through their RESTFul interfaces.
|
||||
|
||||
# Restful Service
|
||||
|
||||
## Introduction
|
||||
|
||||
`[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).
|
||||
`[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.
|
||||
|
||||
<div class="alert" markdown="1">
|
||||
RestfulService currently only supports XML. It has no JSON support at this stage.
|
||||
</div>
|
||||
|
||||
## Examples
|
||||
|
||||
### Creating a new RestfulObject
|
||||
|
||||
:::php
|
||||
//example for using RestfulService to connect and retrive latest twitter status of an user.
|
||||
$twitter = new RestfulService("http://twitter.com/statuses/user_timeline/user.xml", $cache_expiry );
|
||||
$params = array('count' => 1);
|
||||
$twitter->setQueryString($params);
|
||||
$conn = $twitter->request();
|
||||
$msgs = $twitter->getValues($conn, "status");
|
||||
`getWellingtonWeather` queries the Yahoo Weather API for an XML file of the latest weather information. We pass a query
|
||||
string parameter `q` with the search query and then convert the resulting XML data to an `ArrayList` object to display
|
||||
in the template.
|
||||
|
||||
|
||||
### Extending to a new class
|
||||
**mysite/code/Page.php**
|
||||
|
||||
:::php
|
||||
//example for extending RestfulService
|
||||
class FlickrService extends RestfulService {
|
||||
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();
|
||||
|
||||
public function __construct($expiry=NULL){
|
||||
parent::__construct('http://www.flickr.com/services/rest/', $expiry);
|
||||
$this->checkErrors = true;
|
||||
// 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
|
||||
// Set up REST service
|
||||
$service = new RestfulService("http://example.harvestapp.com");
|
||||
$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');
|
||||
$people = $service->getValues($peopleXML, 'user');
|
||||
@ -54,21 +71,20 @@ RestfulService (see [flickrservice](http://silverstripe.org/flickr-module/) and
|
||||
$tasks = $service->getValues($taskXML, 'task');
|
||||
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
### Caching
|
||||
|
||||
To set the cache interval you can pass it as the 2nd argument to constructor.
|
||||
|
||||
:::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
|
||||
|
||||
You can traverse throught document tree to get the values or attribute of a particular node.
|
||||
for example you can traverse
|
||||
You can traverse through document tree to get the values or attribute of a particular node using XPath. Take for example
|
||||
the following XML that is returned.
|
||||
|
||||
:::xml
|
||||
<entries>
|
||||
@ -78,62 +94,77 @@ for example you can traverse
|
||||
<entry id='22'>John</entry>
|
||||
</entries>
|
||||
|
||||
to extract the id attributes of the entries use:
|
||||
To extract the id attributes of the entries use:
|
||||
|
||||
:::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
|
||||
$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
|
||||
|
||||
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.Recommended for retrieving values of namespaced nodes.
|
||||
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.
|
||||
|
||||
<div class="note">
|
||||
This is the recommended method for retrieving values of name spaced nodes.
|
||||
</div>
|
||||
|
||||
:::xml
|
||||
<media:guide>
|
||||
<media:entry id="2030">video</media:entry>
|
||||
</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
|
||||
$this->searchValue($response, "//media:guide/media:entry")
|
||||
$this->searchValue($response, "//media:guide/media:entry");
|
||||
|
||||
// array('video');
|
||||
|
||||
|
||||
## Best Practices
|
||||
|
||||
|
||||
### Handling Errors
|
||||
|
||||
If the web service returned an error (for example, API key not available or inadequate parameters) `[api:RestfulService]`
|
||||
could delgate the error handling to it's descendant class. To handle the errors define a function called errorCatch
|
||||
If the web service returned an error (for example, API key not available or inadequate parameters),
|
||||
`[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
|
||||
// This will raise Youtube API specific error messages (if any).
|
||||
public function errorCatch($response){
|
||||
$err_msg = $response;
|
||||
if(strpos($err_msg, '<') === false) {
|
||||
user_error("YouTube Service Error : $err_msg", E_USER_ERROR);
|
||||
}
|
||||
<?php
|
||||
|
||||
return $response;
|
||||
class MyRestfulService extends RestfulService {
|
||||
|
||||
public function errorCatch($response) {
|
||||
$err_msg = $response;
|
||||
|
||||
if(strpos($err_msg, '<') === false) {
|
||||
user_error("YouTube Service Error : $err_msg", E_USER_ERROR);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
If you want to bypass error handling on your sub-classes you could define that in the constructor.
|
||||
If you want to bypass error handling, define `checkErrors` in the constructor for `RestfulService`
|
||||
|
||||
:::php
|
||||
public function __construct($expiry=NULL){
|
||||
parent::__construct('http://www.flickr.com/services/rest/', $expiry);
|
||||
$this->checkErrors = false; //Set checkErrors to false to bypass error checking
|
||||
<?php
|
||||
|
||||
class MyRestfulService extends RestfulService {
|
||||
|
||||
public function __construct($expiry = NULL) {
|
||||
parent::__construct('http://www.flickr.com/services/rest/', $expiry);
|
||||
|
||||
$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
|
||||
|
||||
`[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>
|
||||
## How to's
|
||||
|
||||
* [Embed an RSS Feed](how_to/embed_rss)
|
||||
|
||||
## 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
|
||||
|
||||
## 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.
|
||||
The `[api:RSSFeed]` class doesn't limit you to generating article based feeds, it is just as easy to create a feed of
|
||||
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,
|
||||
it is just as easy to create a feed of 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 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.
|
||||
<div class="warning" markdown="1">
|
||||
If you wish to generate an RSS feed that contains a `[api:DataObject]`, ensure you define a `AbsoluteLink` method on
|
||||
the object.
|
||||
</div>
|
||||
|
||||
## Usage
|
||||
|
||||
:::php
|
||||
RSSFeed::linkToFeed($link, $title)
|
||||
Including an RSS feed has two steps. First, a `Controller` action which responses with the `XML` and secondly, the other
|
||||
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
|
||||
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().
|
||||
An outline of step one looks like:
|
||||
|
||||
:::php
|
||||
$feed = new RSSFeed(
|
||||
@ -36,63 +35,48 @@ to include the HTML link. Not all arguments are required, see `[api:RSSFeed]` an
|
||||
$etag
|
||||
);
|
||||
|
||||
Creates a new `[api:RSSFeed]` instance to be returned. The arguments notify
|
||||
SilverStripe what values to include in the feed.
|
||||
$feed->outputToBrowser();
|
||||
|
||||
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
|
||||
|
||||
### 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
|
||||
|
||||
You can use `[api:RSSFeed]` to easily create a feed showing your latest Page
|
||||
updates. Update mysite/code/Page.php to something like this:
|
||||
You can use `[api:RSSFeed]` to easily create a feed showing your latest Page updates. The following example adds a page
|
||||
`/home/rss/` which displays an XML file the latest updated pages.
|
||||
|
||||
**mysite/code/Page.php**
|
||||
|
||||
:::php
|
||||
<?php
|
||||
class Page extends SiteTree {}
|
||||
|
||||
..
|
||||
|
||||
class Page_Controller extends ContentController {
|
||||
|
||||
private static $allowed_actions = array('rss');
|
||||
private static $allowed_actions = array(
|
||||
'rss'
|
||||
);
|
||||
|
||||
public function init() {
|
||||
parent::init();
|
||||
|
||||
RSSFeed::linkToFeed($this->Link() . "rss", "10 Most Recently Updated Pages");
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
@ -103,78 +87,109 @@ updates. Update mysite/code/Page.php to something like this:
|
||||
|
||||
### Rendering DataObjects in a RSSFeed
|
||||
|
||||
DataObjects can be rendered in the feed as well, however, since they aren't explicitly
|
||||
`[api:SiteTree]` subclasses we need to include a function `AbsoluteLink` to allow the
|
||||
RSS feed to link through to the item.
|
||||
DataObjects can be rendered in the feed as well, however, since they aren't explicitly `[api:SiteTree]` subclasses we
|
||||
need to include a function `AbsoluteLink` to allow the 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
|
||||
point to a particular page.
|
||||
<div class="info">
|
||||
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
|
||||
defined in the [fifth tutorial](/tutorials/5-dataobject-relationship-management).
|
||||
Take an example, we want to create an RSS feed of all the `Players` objects in our site. We make sure the `AbsoluteLink`
|
||||
method is defined and returns a string to the full website URL.
|
||||
|
||||
:::php
|
||||
<?php
|
||||
class Student extends DataObject {
|
||||
|
||||
class Player extends DataObject {
|
||||
|
||||
public function AbsoluteLink() {
|
||||
// see tutorial 5, students are assigned a project, so the 'link'
|
||||
// to view the student is based on their projects link.
|
||||
return $this->Project()->AbsoluteLink();
|
||||
// assumes players can be accessed at yoursite.com/players/2
|
||||
|
||||
return Controller::join_links(
|
||||
Director::absoluteBaseUrl(),
|
||||
'players',
|
||||
$this->ID
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Then update the Page_Controller class in mysite/code/Page.php to include an RSSFeed
|
||||
for all the students as we've seen before.
|
||||
Then in our controller, we add a new action which returns a the XML list of `Players`.
|
||||
|
||||
:::php
|
||||
<?php
|
||||
|
||||
class Page_Controller extends ContentController {
|
||||
private static $allowed_actions = array('students');
|
||||
|
||||
private static $allowed_actions = array(
|
||||
'players'
|
||||
);
|
||||
|
||||
public function 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(
|
||||
$list = $this->getStudents(),
|
||||
$link = $this->Link("students"),
|
||||
$title = "Students feed"
|
||||
Player::get(),
|
||||
$this->Link("players"),
|
||||
"Players"
|
||||
);
|
||||
|
||||
return $rss->outputToBrowser();
|
||||
}
|
||||
public function getStudents() {
|
||||
return Student::get()->sort("Created", "DESC")->limit(10);
|
||||
}
|
||||
}
|
||||
|
||||
### Customizing the RSS Feed template
|
||||
|
||||
The default template used is framework/templates/RSSFeed.ss and includes
|
||||
displaying titles and links to the content. If you have a particular need
|
||||
for customizing the XML produced (say for additional meta data) use `setTemplate`.
|
||||
The default template used for XML view is `framework/templates/RSSFeed.ss`. This template displays titles and links to
|
||||
the object. To customize the XML produced use `setTemplate`.
|
||||
|
||||
Taking that last example, we would rewrite the students function to include a
|
||||
unique template (write your own XML in themes/yourtheme/templates/Students.ss)
|
||||
Say from that last example we want to include the Players Team in the XML feed we might create the following XML file.
|
||||
|
||||
**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
|
||||
public function students() {
|
||||
|
||||
public function players() {
|
||||
$rss = new RSSFeed(
|
||||
$list = $this->getStudents(),
|
||||
$link = $this->Link("students"),
|
||||
$title = "Students feed"
|
||||
Player::get(),
|
||||
$this->Link("players"),
|
||||
"Players"
|
||||
);
|
||||
$rss->setTemplate('Students');
|
||||
|
||||
$rss->setTemplate('PlayersRss');
|
||||
|
||||
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
|
||||
|
||||
|
@ -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]
|
||||
|
||||
## How-to
|
||||
|
||||
[CHILDREN How_To]
|
@ -5,20 +5,20 @@ summary: Provide your users with advanced search functionality.
|
||||
## Searching for Pages (and Files)
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
requires the MyISAM storage engine, any DataObject you wish to use with Fulltext search must be changed to use MyISAM storage
|
||||
engine.
|
||||
The `[api:MySQLDatabase]` class now defaults to creating tables using the InnoDB storage engine. As Fulltext search in
|
||||
MySQL requires the MyISAM storage engine, any DataObject you wish to use with Fulltext search must be changed to use
|
||||
MyISAM storage engine.
|
||||
|
||||
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
|
||||
|
||||
SilverStripe does not have a built-in method to search through file content (e.g. in PDF or DOC format).
|
||||
You can either extract any textual file content into the `[File](api:File)->Content` property, or use a
|
||||
dedicated search service like the [sphinx module](http://silverstripe.org/sphinx-module).
|
||||
SilverStripe does not have a built-in method to search through file content (e.g. in PDF or DOC format). You can either
|
||||
extract any textual file content into the `[File](api:File)->Content` property, or use a dedicated search service like
|
||||
the [Full module](http://silverstripe.org/sphinx-module).
|
||||
|
||||
## Related
|
||||
|
||||
|
@ -1,48 +1,41 @@
|
||||
title: i18n
|
||||
summary: Display templates and PHP code in different languages based on the preferences of your website users.
|
||||
|
||||
# i18n
|
||||
|
||||
## Introduction
|
||||
|
||||
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
|
||||
as l10n (short for "localization").
|
||||
|
||||
For translating any content managed through the CMS or stored in the database,
|
||||
please use the "[translatable](http://github.com/silverstripe/silverstripe-translatable)" module.
|
||||
For translating any content managed through the CMS or stored in the database, please use the
|
||||
[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:
|
||||
|
||||
* 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).
|
||||
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
|
||||
|
||||
## Usage
|
||||
|
||||
|
||||
### Enabling i18n
|
||||
|
||||
The i18n class is enabled by default.
|
||||
|
||||
### 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
|
||||
want to set.
|
||||
To set the locale you just need to call `[api:i18n::set_locale()]` passing, as a parameter, the name of the locale that
|
||||
you want to set.
|
||||
|
||||
:::php
|
||||
//Example 1: setting the locale
|
||||
i18n::set_locale('de_DE'); //Setting the locale to German (Germany)
|
||||
i18n::set_locale('ca_AD'); //Setting to Catalan (Andorra)
|
||||
// mysite/_config.php
|
||||
i18n::set_locale('de_DE'); // Setting the locale to German (Germany)
|
||||
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
|
||||
these translations are available. See
|
||||
[unicode.org](http://unicode.org/cldr/data/diff/supplemental/languages_and_territories.html) for a complete listing of
|
||||
available locales.
|
||||
these translations are available. See [unicode.org](http://unicode.org/cldr/data/diff/supplemental/languages_and_territories.html)
|
||||
for a complete listing of available locales.
|
||||
|
||||
### 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">
|
||||
|
||||
|
||||
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/).
|
||||
|
||||
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
|
||||
|
||||
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 %>
|
||||
<% loop $Students %>
|
||||
$Name
|
||||
@ -229,15 +223,12 @@ When caching a `<% loop %>` or `<% with %>` with `<%t params %>`. It is importan
|
||||
|
||||
## Collecting text
|
||||
|
||||
To collect all the text in code and template files we have just to visit:
|
||||
|
||||
`http://localhost/dev/tasks/i18nTextCollectorTask`
|
||||
To collect all the text in code and template files we have just to visit: `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
|
||||
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`
|
||||
|
||||
<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:
|
||||
|
||||
![](_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):
|
||||
|
||||
![](_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:
|
||||
|
||||
![](_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.
|
||||
|
||||
![](_images/assets_sync.png)
|
||||
![](/_images/assets_sync.png)
|
||||
|
||||
## Upload
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user