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
|
||||||
@ -48,8 +47,14 @@ form.nostyle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.checkbox label.right{
|
||||||
|
margin: $grid-y/2 0 0 0;
|
||||||
|
display:inline;
|
||||||
|
font-style: normal;
|
||||||
|
color: $color-text;
|
||||||
|
clear:none;
|
||||||
|
}
|
||||||
label {
|
label {
|
||||||
|
|
||||||
&.left {
|
&.left {
|
||||||
float: left;
|
float: left;
|
||||||
display: block;
|
display: block;
|
||||||
@ -59,9 +64,13 @@ form.nostyle {
|
|||||||
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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**
|
||||||
|
|
||||||
|
@ -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.
|
||||||
|
@ -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 };
|
||||||
|
@ -181,6 +181,10 @@ abstract class SS_Database {
|
|||||||
*/
|
*/
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -140,4 +140,50 @@ class ConvertTest extends SapphireTest {
|
|||||||
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