mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
Merge remote branch 'origin/master' into translation-staging
This commit is contained in:
commit
60f94d774c
@ -1,4 +1,6 @@
|
|||||||
## Overview
|
## SilverStripe Framework
|
||||||
|
|
||||||
|
[![Build Status](https://secure.travis-ci.org/silverstripe/sapphire.png)](http://travis-ci.org/silverstripe/sapphire)
|
||||||
|
|
||||||
PHP5 framework forming the base for the SilverStripe CMS ([http://silverstripe.org](http://silverstripe.org)).
|
PHP5 framework forming the base for the SilverStripe CMS ([http://silverstripe.org](http://silverstripe.org)).
|
||||||
Requires a [`silverstripe-installer`](http://github.com/silverstripe/silverstripe-installer) base project. Typically used alongside the [`cms`](http://github.com/silverstripe/silverstripe-cms) module.
|
Requires a [`silverstripe-installer`](http://github.com/silverstripe/silverstripe-installer) base project. Typically used alongside the [`cms`](http://github.com/silverstripe/silverstripe-cms) module.
|
||||||
|
@ -149,8 +149,9 @@ form.nostyle input.text, form.nostyle textarea, form.nostyle select, form.nostyl
|
|||||||
.field:after { content: "\0020"; display: block; height: 0; clear: both; overflow: hidden; visibility: hidden; }
|
.field:after { content: "\0020"; display: block; height: 0; clear: both; overflow: hidden; visibility: hidden; }
|
||||||
.field.nolabel .middleColumn { margin-left: 0; }
|
.field.nolabel .middleColumn { margin-left: 0; }
|
||||||
.field.nolabel .help { margin-left: 0; }
|
.field.nolabel .help { margin-left: 0; }
|
||||||
|
.field.checkbox label.right { margin: 4px 0 0 0; display: inline; font-style: normal; color: #444444; clear: none; }
|
||||||
.field label.left { float: left; display: block; width: 176px; padding: 8px 8px 8px 0; line-height: 16px; font-weight: bold; text-shadow: 1px 1px 0 white; }
|
.field label.left { float: left; display: block; width: 176px; padding: 8px 8px 8px 0; line-height: 16px; font-weight: bold; text-shadow: 1px 1px 0 white; }
|
||||||
.field label.right { cursor: pointer; }
|
.field label.right { cursor: pointer; clear: both; color: #777777; display: block; font-style: italic; margin: 4px 0 0 184px; }
|
||||||
.field .middleColumn { margin-left: 184px; }
|
.field .middleColumn { margin-left: 184px; }
|
||||||
.field span.readonly { padding-top: 8px; line-height: 16px; display: block; }
|
.field span.readonly { padding-top: 8px; line-height: 16px; display: block; }
|
||||||
.field .help { clear: both; color: #777777; display: block; font-style: italic; margin: 4px 0 0 184px; }
|
.field .help { clear: both; color: #777777; display: block; font-style: italic; margin: 4px 0 0 184px; }
|
||||||
@ -381,7 +382,7 @@ body.cms { overflow: hidden; }
|
|||||||
.cms-add-form #PageType ul li .description { font-style: italic; }
|
.cms-add-form #PageType ul li .description { font-style: italic; }
|
||||||
|
|
||||||
/** -------------------------------------------- Content toolbar -------------------------------------------- */
|
/** -------------------------------------------- Content toolbar -------------------------------------------- */
|
||||||
.cms-content-toolbar { display: block; margin: 0 0 15px 0; border-bottom: 1px solid rgba(201, 205, 206, 0.8); -webkit-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.8); -moz-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.8); -o-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.8); box-shadow: 0 1px 0 rgba(255, 255, 255, 0.8); *zoom: 1; /* smaller treedropdown */ }
|
.cms-content-toolbar { min-height: 35px; display: block; margin: 0 0 15px 0; border-bottom: 1px solid rgba(201, 205, 206, 0.8); -webkit-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.8); -moz-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.8); -o-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.8); box-shadow: 0 1px 0 rgba(255, 255, 255, 0.8); *zoom: 1; /* smaller treedropdown */ }
|
||||||
.cms-content-toolbar:after { content: "\0020"; display: block; height: 0; clear: both; overflow: hidden; visibility: hidden; }
|
.cms-content-toolbar:after { content: "\0020"; display: block; height: 0; clear: both; overflow: hidden; visibility: hidden; }
|
||||||
.cms-content-toolbar .cms-tree-view-modes { float: right; padding-top: 5px; }
|
.cms-content-toolbar .cms-tree-view-modes { float: right; padding-top: 5px; }
|
||||||
.cms-content-toolbar .cms-tree-view-modes * { display: inline-block; }
|
.cms-content-toolbar .cms-tree-view-modes * { display: inline-block; }
|
||||||
|
@ -22,7 +22,6 @@ form.nostyle {
|
|||||||
//TODO: use single border line with shadow instead:: http://daverupert.com/2011/06/two-tone-borders-with-css3/
|
//TODO: use single border line with shadow instead:: http://daverupert.com/2011/06/two-tone-borders-with-css3/
|
||||||
//overflow: hidden;
|
//overflow: hidden;
|
||||||
|
|
||||||
|
|
||||||
// bottom padding accounts for the border and we have a negative
|
// bottom padding accounts for the border and we have a negative
|
||||||
// margin with a postive padding to ensure the bottom border extends
|
// margin with a postive padding to ensure the bottom border extends
|
||||||
// over the edges
|
// over the edges
|
||||||
@ -47,9 +46,15 @@ form.nostyle {
|
|||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
label {
|
&.checkbox label.right{
|
||||||
|
margin: $grid-y/2 0 0 0;
|
||||||
|
display:inline;
|
||||||
|
font-style: normal;
|
||||||
|
color: $color-text;
|
||||||
|
clear:none;
|
||||||
|
}
|
||||||
|
label {
|
||||||
&.left {
|
&.left {
|
||||||
float: left;
|
float: left;
|
||||||
display: block;
|
display: block;
|
||||||
@ -58,11 +63,15 @@ form.nostyle {
|
|||||||
line-height: $grid-y * 2;
|
line-height: $grid-y * 2;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
@include text-shadow(1px 1px 0 $color-text-shadow);
|
@include text-shadow(1px 1px 0 $color-text-shadow);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.right {
|
&.right {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
clear: both;
|
||||||
|
color: lighten($color-text, 20%);
|
||||||
|
display: block;
|
||||||
|
font-style: italic;
|
||||||
|
margin: $grid-y/2 0 0 $grid-x*23;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.middleColumn {
|
.middleColumn {
|
||||||
|
@ -596,7 +596,7 @@ body.cms {
|
|||||||
* -------------------------------------------- */
|
* -------------------------------------------- */
|
||||||
|
|
||||||
.cms-content-toolbar {
|
.cms-content-toolbar {
|
||||||
|
min-height:35px;
|
||||||
display: block;
|
display: block;
|
||||||
margin: 0 0 15px 0;
|
margin: 0 0 15px 0;
|
||||||
|
|
||||||
|
@ -317,4 +317,15 @@ class Convert {
|
|||||||
$f = URLSegmentFilter::create();
|
$f = URLSegmentFilter::create();
|
||||||
return $f->filter($title);
|
return $f->filter($title);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalises newline sequences to conform to (an) OS specific format.
|
||||||
|
* @param string $data Text containing potentially mixed formats of newline
|
||||||
|
* sequences including \r, \r\n, \n, or unicode newline characters
|
||||||
|
* @param string $nl The newline sequence to normalise to. Defaults to that
|
||||||
|
* specified by the current OS
|
||||||
|
*/
|
||||||
|
public static function nl2os($data, $nl = PHP_EOL) {
|
||||||
|
return preg_replace('~\R~u', $nl, $data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -93,6 +93,8 @@ class TidyHTMLCleaner extends HTMLCleaner {
|
|||||||
public function cleanHTML($content) {
|
public function cleanHTML($content) {
|
||||||
$tidy = new tidy();
|
$tidy = new tidy();
|
||||||
$output = $tidy->repairString($content, $this->config);
|
$output = $tidy->repairString($content, $this->config);
|
||||||
return $output;
|
|
||||||
|
// Clean leading/trailing whitespace
|
||||||
|
return preg_replace('/(^\s+)|(\s+$)/', '', $output);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,11 +86,11 @@ class Deprecation {
|
|||||||
protected static function get_calling_module_from_trace($backtrace) {
|
protected static function get_calling_module_from_trace($backtrace) {
|
||||||
if (!isset($backtrace[1]['file'])) return;
|
if (!isset($backtrace[1]['file'])) return;
|
||||||
|
|
||||||
$callingfile = $backtrace[1]['file'];
|
$callingfile = realpath($backtrace[1]['file']);
|
||||||
|
|
||||||
global $manifest;
|
global $manifest;
|
||||||
foreach ($manifest->getModules() as $name => $path) {
|
foreach ($manifest->getModules() as $name => $path) {
|
||||||
if (strpos($callingfile, $path) === 0) {
|
if (strpos($callingfile, realpath($path)) === 0) {
|
||||||
return $name;
|
return $name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
189
docs/en/reference/uploadfield.md
Normal file
189
docs/en/reference/uploadfield.md
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
# UploadField
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
The UploadField will let you upload one or multiple files of all types,
|
||||||
|
including images. But that's not all it does - it will also link the
|
||||||
|
uploaded file(s) to an existing relation and let you edit the linked files
|
||||||
|
as well. That makes it flexible enough to sometimes even replace the Gridfield,
|
||||||
|
like for instance in creating and managing a simple gallery.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
The UploadField can be used in two ways:
|
||||||
|
|
||||||
|
### Single fileupload
|
||||||
|
|
||||||
|
The following example adds an UploadField to a page for single fileupload,
|
||||||
|
based on a has_one relation:
|
||||||
|
|
||||||
|
:::php
|
||||||
|
class GalleryPage extends Page {
|
||||||
|
|
||||||
|
static $has_one = array(
|
||||||
|
'SingleImage' => 'Image'
|
||||||
|
);
|
||||||
|
|
||||||
|
function getCMSFields() {
|
||||||
|
|
||||||
|
$fields = parent::getCMSFields();
|
||||||
|
|
||||||
|
$fields->addFieldToTab(
|
||||||
|
'Root.Upload',
|
||||||
|
$uploadField = new UploadField(
|
||||||
|
$name = 'SingleImage',
|
||||||
|
$title = 'Upload a single image'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return $fields;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
The UploadField will autodetect the relation based on it's `name` property, and
|
||||||
|
save it into the GalleyPages' `SingleImageID` field. Setting the
|
||||||
|
`allowedMaxFileNumber` to 1 will make sure that only one image can ever be
|
||||||
|
uploaded and linked to the relation.
|
||||||
|
|
||||||
|
### Multiple fileupload
|
||||||
|
|
||||||
|
Enable multiple fileuploads by using a many_many relation. Again, the
|
||||||
|
UploadField will detect the relation based on its $name property value:
|
||||||
|
|
||||||
|
:::php
|
||||||
|
class GalleryPage extends Page {
|
||||||
|
|
||||||
|
static $many_many = array(
|
||||||
|
'GalleryImages' => 'Image'
|
||||||
|
);
|
||||||
|
|
||||||
|
function getCMSFields() {
|
||||||
|
|
||||||
|
$fields = parent::getCMSFields();
|
||||||
|
|
||||||
|
$fields->addFieldToTab(
|
||||||
|
'Root.Upload',
|
||||||
|
$uploadField = new UploadField(
|
||||||
|
$name = 'GalleryImages',
|
||||||
|
$title = 'Upload one or more images (max 10 in total)'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
$uploadField->setConfig('allowedMaxFileNumber', 10);
|
||||||
|
|
||||||
|
return $fields;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class GalleryPage_Controller extends Page_Controller {
|
||||||
|
}
|
||||||
|
|
||||||
|
WARNING: Currently the UploadField doesn't fully support has_many relations, so use a many_many relation instead!
|
||||||
|
|
||||||
|
## Set a custom folder
|
||||||
|
|
||||||
|
This example will save all uploads in the `/assets/customfolder/` folder. If
|
||||||
|
the folder doesn't exist, it will be created.
|
||||||
|
|
||||||
|
:::php
|
||||||
|
$fields->addFieldToTab(
|
||||||
|
'Root.Upload',
|
||||||
|
$uploadField = new UploadField(
|
||||||
|
$name = 'GalleryImages',
|
||||||
|
$title = 'Please upload one or more images' )
|
||||||
|
);
|
||||||
|
$uploadField->setFolderName('customfolder');
|
||||||
|
|
||||||
|
## Limit the allowed filetypes
|
||||||
|
|
||||||
|
`AllowedExtensions` is by default `File::$allowed_extensions` but can be overwritten for each UploadField:
|
||||||
|
|
||||||
|
:::php
|
||||||
|
$uploadField->getValidator()->setAllowedExtensions(array('jpg', 'jpeg', 'png', 'gif'));
|
||||||
|
|
||||||
|
|
||||||
|
## Limit the maximum file size
|
||||||
|
|
||||||
|
`AllowedMaxFileSize` is by default set to the lower value of the 2 php.ini configurations: `upload_max_filesize` and `post_max_size`
|
||||||
|
The value is set as bytes.
|
||||||
|
|
||||||
|
NOTE: this only sets the configuration for your UploadField, this does NOT change your server upload settings, so if your server is set to only allow 1 MB and you set the UploadFIeld to 2 MB, uploads will not work.
|
||||||
|
|
||||||
|
:::php
|
||||||
|
$sizeMB = 2; // 2 MB
|
||||||
|
$size = $sizeMB * 1024 * 1024; // 2 MB in bytes
|
||||||
|
$this->getValidator()->setAllowedMaxFileSize($size);
|
||||||
|
|
||||||
|
## Other configuration settings
|
||||||
|
|
||||||
|
### Preview dimensions
|
||||||
|
|
||||||
|
Set the dimensions of the image preview. By default the max width is set to 80
|
||||||
|
and the max height is set to 60.
|
||||||
|
|
||||||
|
:::php
|
||||||
|
$uploadField->setConfig('previewMaxWidth', 100);
|
||||||
|
$uploadField->setConfig('previewMaxHeight', 100);
|
||||||
|
|
||||||
|
### Automatic or manual upload
|
||||||
|
|
||||||
|
By default, the UploadField will try to automatically upload all selected files.
|
||||||
|
Setting the `autoUpload` property to false, will present you with a list of
|
||||||
|
selected files that you can then upload manually one by one:
|
||||||
|
|
||||||
|
:::php
|
||||||
|
$uploadField->setConfig('autoUpload', false);
|
||||||
|
|
||||||
|
### Build a simple gallery
|
||||||
|
|
||||||
|
A gallery most times needs more then simple images. You might want to add a
|
||||||
|
description, or maybe some settings to define a transition effect for each slide.
|
||||||
|
First create a
|
||||||
|
[DataExtension](http://doc.silverstripe.org/framework/en/reference/dataextension)
|
||||||
|
like this:
|
||||||
|
|
||||||
|
:::php
|
||||||
|
class GalleryImage extends DataExtension {
|
||||||
|
|
||||||
|
static $db = array(
|
||||||
|
'Description' => 'Text'
|
||||||
|
);
|
||||||
|
|
||||||
|
public static $belongs_many_many = array(
|
||||||
|
'GalleryPage' => 'GalleryPage'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Now register the DataExtension for the Image class in your _config.php:
|
||||||
|
|
||||||
|
:::php
|
||||||
|
Object::add_extension('Image', 'GalleryImage');
|
||||||
|
|
||||||
|
NOTE: although you can subclass the Image class instead of using a DataExtension, this is not advisable. For instance: when using a subclass, the 'From files' button will only return files that were uploaded for that subclass, it won't recognize any other images!
|
||||||
|
### Edit uploaded images
|
||||||
|
|
||||||
|
By default the UploadField will let you edit the following fields: *Title,
|
||||||
|
Filename, Owner and Folder*. The `fileEditFields` configuration setting allows
|
||||||
|
you you alter these settings. One way to go about this is create a
|
||||||
|
`getCustomFields` function in your GalleryImage object like this:
|
||||||
|
|
||||||
|
:::php
|
||||||
|
class GalleryImage extends DataExtension {
|
||||||
|
...
|
||||||
|
|
||||||
|
function getCustomFields() {
|
||||||
|
$fields = new FieldList();
|
||||||
|
$fields->push(new TextField('Title', 'Title'));
|
||||||
|
$fields->push(new TextareaField('Description', 'Description'));
|
||||||
|
return $fields;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Then, in your GalleryPage, tell the UploadField to use this function:
|
||||||
|
|
||||||
|
:::php
|
||||||
|
$uploadField->setConfig('fileEditFields', 'getCustomFields');
|
||||||
|
|
||||||
|
In a similar fashion you can use 'fileEditActions' to set the actions for the
|
||||||
|
editform, or 'fileEditValidator' to determine the validator (eg RequiredFields).
|
||||||
|
|
||||||
|
## TODO: Using the UploadField in a frontend form
|
||||||
|
|
||||||
|
*At this moment the UploadField not yet fully supports being used on a frontend
|
||||||
|
form.*
|
@ -226,15 +226,6 @@ since 1/1/2011.
|
|||||||
'LastVisited:GreaterThan' => '2011-01-01'
|
'LastVisited:GreaterThan' => '2011-01-01'
|
||||||
));
|
));
|
||||||
|
|
||||||
If you wish to match against any of a number of columns, you can list several field names, separated by commas.
|
|
||||||
This will return all members whose first name or surname contain the string 'sam'.
|
|
||||||
|
|
||||||
:::php
|
|
||||||
$members = Member::get()->filter(array(
|
|
||||||
'FirstName,Surname:PartialMatch' => 'sam'
|
|
||||||
));
|
|
||||||
|
|
||||||
|
|
||||||
### Subtract
|
### Subtract
|
||||||
|
|
||||||
You can subtract entries from a DataList by passing in another DataList to `subtract()`
|
You can subtract entries from a DataList by passing in another DataList to `subtract()`
|
||||||
|
@ -145,8 +145,8 @@ or placed between SilverStripe template tags:
|
|||||||
|
|
||||||
**Flushing the cache**
|
**Flushing the cache**
|
||||||
|
|
||||||
Whenever we edit a template file, we need to append *?flush=all* onto the end of the URL, e.g.
|
Whenever we edit a template file, we need to append *?flush=1* onto the end of the URL, e.g.
|
||||||
http://localhost/your_site_name/?flush=all. SilverStripe stores template files in a cache for quicker load times. Whenever there are
|
http://localhost/your_site_name/?flush=1. SilverStripe stores template files in a cache for quicker load times. Whenever there are
|
||||||
changes to the template, we must flush the cache in order for the changes to take effect.
|
changes to the template, we must flush the cache in order for the changes to take effect.
|
||||||
|
|
||||||
## The Navigation System
|
## The Navigation System
|
||||||
@ -343,7 +343,7 @@ Create a new file *HomePage.php* in *mysite/code*. Copy the following code into
|
|||||||
|
|
||||||
|
|
||||||
Every page type also has a database table corresponding to it. Every time we modify the database, we need to rebuild it.
|
Every page type also has a database table corresponding to it. Every time we modify the database, we need to rebuild it.
|
||||||
We can do this by going to [http://localhost/your_site_name/dev/build?flush=all](http://localhost/your_site_name/dev/build?flush=1) (replace *localhost/your_site_name* with your own domain name if applicable).
|
We can do this by going to [http://localhost/your_site_name/dev/build](http://localhost/your_site_name/dev/build) (replace *localhost/your_site_name* with your own domain name if applicable).
|
||||||
|
|
||||||
It may take a moment, so be patient. This add tables and fields needed by your site, and modifies any structures that have changed. It
|
It may take a moment, so be patient. This add tables and fields needed by your site, and modifies any structures that have changed. It
|
||||||
does this non-destructively - it will never delete your data.
|
does this non-destructively - it will never delete your data.
|
||||||
@ -366,7 +366,7 @@ It always tries to use the most specific template in an inheritance chain.
|
|||||||
|
|
||||||
### Creating a new template
|
### Creating a new template
|
||||||
|
|
||||||
To create a new template layout, create a copy of *Page.ss* (found in *themes/simple/templates/Layout*) and call it *HomePage.ss*. If we flush the cache (*?flush=all*), SilverStripe should now be using *HomePage.ss* for the homepage, and *Page.ss* for the rest of the site. Now let's customize the *HomePage* template.
|
To create a new template layout, create a copy of *Page.ss* (found in *themes/simple/templates/Layout*) and call it *HomePage.ss*. If we flush the cache (*?flush=1*), SilverStripe should now be using *HomePage.ss* for the homepage, and *Page.ss* for the rest of the site. Now let's customize the *HomePage* template.
|
||||||
|
|
||||||
First, we don't need the breadcrumbs and the secondary menu for the homepage. Let's remove them:
|
First, we don't need the breadcrumbs and the secondary menu for the homepage. Let's remove them:
|
||||||
:::ss
|
:::ss
|
||||||
|
@ -90,7 +90,7 @@ to be children of the page in the site tree. As we only want **news articles** i
|
|||||||
|
|
||||||
We will be introduced to other fields like this as we progress; there is a full list in the documentation for `[api:SiteTree]`.
|
We will be introduced to other fields like this as we progress; there is a full list in the documentation for `[api:SiteTree]`.
|
||||||
|
|
||||||
Now that we have created our page types, we need to let SilverStripe rebuild the database: [http://localhost/your_site_name/dev/build?flush=all](http://localhost/your_site_name/dev/build?flush=all). SilverStripe should detect that there are two new page types, and add them to the list of page types in the database.
|
Now that we have created our page types, we need to let SilverStripe rebuild the database: [http://localhost/your_site_name/dev/build](http://localhost/your_site_name/dev/build). SilverStripe should detect that there are two new page types, and add them to the list of page types in the database.
|
||||||
|
|
||||||
<div class="hint" markdown="1">
|
<div class="hint" markdown="1">
|
||||||
It is SilverStripe convention to suffix general page types with "Page", and page types that hold other page types with
|
It is SilverStripe convention to suffix general page types with "Page", and page types that hold other page types with
|
||||||
@ -177,7 +177,7 @@ There are many more fields available in the default installation, listed in ["fo
|
|||||||
return $fields;
|
return $fields;
|
||||||
|
|
||||||
|
|
||||||
Finally, we return the fields to the CMS. If we flush the cache (by adding ?flush=all at the end of the URL), we will be able to edit the fields in the CMS.
|
Finally, we return the fields to the CMS. If we flush the cache (by adding ?flush=1 at the end of the URL), we will be able to edit the fields in the CMS.
|
||||||
|
|
||||||
Now that we have created our page types, let's add some content. Go into the CMS and create an *ArticleHolder* page named "News", then create a few *ArticlePage*'s within it.
|
Now that we have created our page types, let's add some content. Go into the CMS and create an *ArticleHolder* page named "News", then create a few *ArticlePage*'s within it.
|
||||||
|
|
||||||
@ -469,7 +469,7 @@ the CMS.
|
|||||||
|
|
||||||
![](_images/tutorial2_photo.jpg)
|
![](_images/tutorial2_photo.jpg)
|
||||||
|
|
||||||
Rebuild the database ([http://localhost/your_site_name/dev/build?flush=1](http://localhost/your_site_name/dev/build?flush=1)) and open the CMS. Create
|
Rebuild the database ([http://localhost/your_site_name/dev/build](http://localhost/your_site_name/dev/build)) and open the CMS. Create
|
||||||
a new *StaffHolder* called "Staff", and create some *StaffPage*s in it.
|
a new *StaffHolder* called "Staff", and create some *StaffPage*s in it.
|
||||||
|
|
||||||
![](_images/tutorial2_create-staff.jpg)
|
![](_images/tutorial2_create-staff.jpg)
|
||||||
|
@ -156,7 +156,7 @@ Add the following code to the existing `form.css` file:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
All going according to plan, if you visit [http://localhost/your_site_name/home?flush=all](http://localhost/your_site_name/home?flush=all) it should look something like this:
|
All going according to plan, if you visit [http://localhost/your_site_name/home?flush=1](http://localhost/your_site_name/home?flush=1) it should look something like this:
|
||||||
|
|
||||||
![](_images/tutorial3_pollform.jpg)
|
![](_images/tutorial3_pollform.jpg)
|
||||||
|
|
||||||
@ -179,7 +179,7 @@ If you recall, in the [second tutorial](2-extending-a-basic-site) we said that a
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
If we then rebuild the database ([http://localhost/your_site_name/dev/build?flush=all](http://localhost/your_site_name/dev/build?flush=all)), we will see that the *BrowserPollSubmission* table is created. Now we just need to define 'doBrowserPoll' on *HomePage_Controller*:
|
If we then rebuild the database ([http://localhost/your_site_name/dev/build](http://localhost/your_site_name/dev/build)), we will see that the *BrowserPollSubmission* table is created. Now we just need to define 'doBrowserPoll' on *HomePage_Controller*:
|
||||||
|
|
||||||
**mysite/code/HomePage.php**
|
**mysite/code/HomePage.php**
|
||||||
|
|
||||||
@ -349,4 +349,4 @@ We use the normal tactic of putting the data into an unordered list and using CS
|
|||||||
|
|
||||||
In this tutorial we have explored custom php forms, and displayed result sets through Grouped Lists. We have briefly covered the different approaches to creating and using forms. Whether you decide to use the [userforms module](http://silverstripe.org/user-forms-module) or create a form in PHP depends on the situation and flexibility required.
|
In this tutorial we have explored custom php forms, and displayed result sets through Grouped Lists. We have briefly covered the different approaches to creating and using forms. Whether you decide to use the [userforms module](http://silverstripe.org/user-forms-module) or create a form in PHP depends on the situation and flexibility required.
|
||||||
|
|
||||||
[Next Tutorial >>](4-site-search)
|
[Next Tutorial >>](4-site-search)
|
||||||
|
@ -18,7 +18,7 @@ This will enable fulltext search on page content as well as names of all files i
|
|||||||
:::php
|
:::php
|
||||||
FulltextSearchable::enable();
|
FulltextSearchable::enable();
|
||||||
|
|
||||||
After including that in your `_config.php` you will need to rebuild the database by visiting [http://localhost/your_site_name/home?flush=all](http://localhost/your_site_name/home?flush=all) in your web browser (replace localhost/your_site_name with a domain if applicable). This will add fulltext search columns.
|
After including that in your `_config.php` you will need to rebuild the database by visiting [http://localhost/your_site_name/dev/build](http://localhost/your_site_name/dev/build) in your web browser (replace localhost/your_site_name with a domain if applicable). This will add fulltext search columns.
|
||||||
|
|
||||||
The actual search form code is already provided in FulltextSearchable so when you add the enable line above to your `_config.php` you can add your form as `$SearchForm`.
|
The actual search form code is already provided in FulltextSearchable so when you add the enable line above to your `_config.php` you can add your form as `$SearchForm`.
|
||||||
|
|
||||||
@ -146,7 +146,7 @@ class.
|
|||||||
<% end_if %>
|
<% end_if %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
Then finally add ?flush=all to the URL and you should see the new template.
|
Then finally add ?flush=1 to the URL and you should see the new template.
|
||||||
|
|
||||||
|
|
||||||
![](_images/tutorial4_search.jpg)
|
![](_images/tutorial4_search.jpg)
|
||||||
|
@ -72,7 +72,7 @@ we don't need to define any additional ones for our purposes.
|
|||||||
|
|
||||||
Now that we have our models defined in PHP code,
|
Now that we have our models defined in PHP code,
|
||||||
we need to tell the database to create the related tables.
|
we need to tell the database to create the related tables.
|
||||||
Trigger a rebuild through *dev/build?flush=all* before you
|
Trigger a rebuild through *dev/build* before you
|
||||||
proceed to the next part of this tutorial.
|
proceed to the next part of this tutorial.
|
||||||
|
|
||||||
### Organizing pages: ProjectHolder
|
### Organizing pages: ProjectHolder
|
||||||
@ -318,7 +318,7 @@ a named list of object.
|
|||||||
|
|
||||||
Navigate to the holder page through your website navigation,
|
Navigate to the holder page through your website navigation,
|
||||||
or the "Preview" feature in the CMS. You should see a list of all projects now.
|
or the "Preview" feature in the CMS. You should see a list of all projects now.
|
||||||
Add `?flush=all` to the page URL to force a refresh of the template cache.
|
Add `?flush=1` to the page URL to force a refresh of the template cache.
|
||||||
|
|
||||||
To get a list of all projects, we've looped through the "Children" list,
|
To get a list of all projects, we've looped through the "Children" list,
|
||||||
which is a relationship we didn't define explictly.
|
which is a relationship we didn't define explictly.
|
||||||
@ -419,4 +419,4 @@ we suggest some excercises to make the solution more flexible:
|
|||||||
and avoid any duplication between the two subclasses.
|
and avoid any duplication between the two subclasses.
|
||||||
* Render mentor details in their own template
|
* Render mentor details in their own template
|
||||||
* Change the `GridField` to list only five records per page (the default is 20).
|
* Change the `GridField` to list only five records per page (the default is 20).
|
||||||
This configuration is stored in the `[api:GridFieldPaginator]` component
|
This configuration is stored in the `[api:GridFieldPaginator]` component
|
||||||
|
@ -522,7 +522,8 @@ class i18nTextCollector_Writer_Php implements i18nTextCollector_Writer {
|
|||||||
$php .= (count($entitySpec) == 1) ? var_export($entitySpec[0], true) : var_export($entitySpec, true);
|
$php .= (count($entitySpec) == 1) ? var_export($entitySpec[0], true) : var_export($entitySpec, true);
|
||||||
$php .= ";$eol";
|
$php .= ";$eol";
|
||||||
|
|
||||||
return $php;
|
// Normalise linebreaks due to fix var_export output
|
||||||
|
return Convert::nl2os($php, $eol);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -302,7 +302,7 @@
|
|||||||
},
|
},
|
||||||
type: "GET",
|
type: "GET",
|
||||||
url: suggestionUrl,
|
url: suggestionUrl,
|
||||||
data: form.serialize()+'&'+escape(searchField.attr('name'))+'='+escape(searchField.val()),
|
data: escape(searchField.attr('name'))+'='+escape(searchField.val()),
|
||||||
success: function(data) {
|
success: function(data) {
|
||||||
response( $.map(JSON.parse(data), function( name, id ) {
|
response( $.map(JSON.parse(data), function( name, id ) {
|
||||||
return { label: name, value: name, id: id };
|
return { label: name, value: name, id: id };
|
||||||
|
@ -180,8 +180,12 @@ abstract class SS_Database {
|
|||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $indexList;
|
protected $indexList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keeps track whether we are currently updating the schema.
|
||||||
|
*/
|
||||||
|
protected $schemaIsUpdating = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Large array structure that represents a schema update transaction
|
* Large array structure that represents a schema update transaction
|
||||||
*/
|
*/
|
||||||
@ -193,6 +197,7 @@ abstract class SS_Database {
|
|||||||
* Once
|
* Once
|
||||||
*/
|
*/
|
||||||
public function beginSchemaUpdate() {
|
public function beginSchemaUpdate() {
|
||||||
|
$this->schemaIsUpdating = true;
|
||||||
$this->tableList = array();
|
$this->tableList = array();
|
||||||
$tables = $this->tableList();
|
$tables = $this->tableList();
|
||||||
foreach($tables as $table) $this->tableList[strtolower($table)] = $table;
|
foreach($tables as $table) $this->tableList[strtolower($table)] = $table;
|
||||||
@ -221,6 +226,7 @@ abstract class SS_Database {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
$this->schemaUpdateTransaction = null;
|
$this->schemaUpdateTransaction = null;
|
||||||
|
$this->schemaIsUpdating = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -228,6 +234,14 @@ abstract class SS_Database {
|
|||||||
*/
|
*/
|
||||||
public function cancelSchemaUpdate() {
|
public function cancelSchemaUpdate() {
|
||||||
$this->schemaUpdateTransaction = null;
|
$this->schemaUpdateTransaction = null;
|
||||||
|
$this->schemaIsUpdating = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if we are during a schema update.
|
||||||
|
*/
|
||||||
|
function isSchemaUpdating() {
|
||||||
|
return $this->schemaIsUpdating;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -139,5 +139,51 @@ class ConvertTest extends SapphireTest {
|
|||||||
$this->assertEquals('foos-bar-2', Convert::raw2url('foo\'s [bar] (2)'));
|
$this->assertEquals('foos-bar-2', Convert::raw2url('foo\'s [bar] (2)'));
|
||||||
URLSegmentFilter::$default_allow_multibyte = $orig;
|
URLSegmentFilter::$default_allow_multibyte = $orig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function for comparing characters with significant whitespaces
|
||||||
|
* @param type $expected
|
||||||
|
* @param type $actual
|
||||||
|
*/
|
||||||
|
protected function assertEqualsQuoted($expected, $actual) {
|
||||||
|
$message = sprintf(
|
||||||
|
"Expected \"%s\" but given \"%s\"",
|
||||||
|
addcslashes($expected, "\r\n"),
|
||||||
|
addcslashes($actual, "\r\n")
|
||||||
|
);
|
||||||
|
$this->assertEquals($expected, $actual, $message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNL2OS() {
|
||||||
|
|
||||||
|
foreach(array("\r\n", "\r", "\n") as $nl) {
|
||||||
|
|
||||||
|
// Base case: no action
|
||||||
|
$this->assertEqualsQuoted(
|
||||||
|
"Base case",
|
||||||
|
Convert::nl2os("Base case", $nl)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Mixed formats
|
||||||
|
$this->assertEqualsQuoted(
|
||||||
|
"Test{$nl}Text{$nl}Is{$nl}{$nl}Here{$nl}.",
|
||||||
|
Convert::nl2os("Test\rText\r\nIs\n\rHere\r\n.", $nl)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test that multiple runs are non-destructive
|
||||||
|
$expected = "Test{$nl}Text{$nl}Is{$nl}{$nl}Here{$nl}.";
|
||||||
|
$this->assertEqualsQuoted(
|
||||||
|
$expected,
|
||||||
|
Convert::nl2os($expected, $nl)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Check repeated sequence behaves correctly
|
||||||
|
$expected = "{$nl}{$nl}{$nl}{$nl}{$nl}{$nl}{$nl}{$nl}";
|
||||||
|
$input = "\r\r\n\r\r\n\n\n\n\r";
|
||||||
|
$this->assertEqualsQuoted(
|
||||||
|
$expected,
|
||||||
|
Convert::nl2os($input, $nl)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,13 +11,13 @@ class HTMLCleanerTest extends SapphireTest {
|
|||||||
|
|
||||||
if ($cleaner) {
|
if ($cleaner) {
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
$cleaner->cleanHTML('<p>wrong <b>nesting</i></p>' . "\n"),
|
$cleaner->cleanHTML('<p>wrong <b>nesting</i></p>'),
|
||||||
'<p>wrong <b>nesting</b></p>' . "\n",
|
'<p>wrong <b>nesting</b></p>',
|
||||||
"HTML cleaned properly"
|
"HTML cleaned properly"
|
||||||
);
|
);
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
$cleaner->cleanHTML('<p>unclosed paragraph' . "\n"),
|
$cleaner->cleanHTML('<p>unclosed paragraph'),
|
||||||
'<p>unclosed paragraph</p>' . "\n",
|
'<p>unclosed paragraph</p>',
|
||||||
"HTML cleaned properly"
|
"HTML cleaned properly"
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
@ -413,7 +413,7 @@ de:
|
|||||||
OtherEntityName: 'Other Text'
|
OtherEntityName: 'Other Text'
|
||||||
|
|
||||||
YAML;
|
YAML;
|
||||||
$this->assertEquals($yaml, $writer->getYaml($entities, 'de'));
|
$this->assertEquals($yaml, Convert::nl2os($writer->getYaml($entities, 'de')));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testCollectFromIncludedTemplates() {
|
public function testCollectFromIncludedTemplates() {
|
||||||
|
@ -62,6 +62,22 @@ class DatabaseTest extends SapphireTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function testIsSchemaUpdating() {
|
||||||
|
$db = DB::getConn();
|
||||||
|
|
||||||
|
$this->assertFalse($db->isSchemaUpdating(), 'Before the transaction the flag is false.');
|
||||||
|
|
||||||
|
$db->beginSchemaUpdate();
|
||||||
|
$this->assertTrue($db->isSchemaUpdating(), 'During the transaction the flag is true.');
|
||||||
|
|
||||||
|
$db->endSchemaUpdate();
|
||||||
|
$this->assertFalse($db->isSchemaUpdating(), 'After the transaction the flag is false.');
|
||||||
|
|
||||||
|
$db->beginSchemaUpdate();
|
||||||
|
$db->cancelSchemaUpdate();
|
||||||
|
$this->assertFalse($db->doesSchemaNeedUpdating(), 'After cancelling the transaction the flag is false');
|
||||||
|
}
|
||||||
|
|
||||||
public function testSchemaUpdateChecking() {
|
public function testSchemaUpdateChecking() {
|
||||||
$db = DB::getConn();
|
$db = DB::getConn();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user