mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
Add codesniffer that ensures indentation is with tabs.
This commit is contained in:
parent
ed11970ede
commit
b0121b541c
@ -22,7 +22,8 @@ before_script:
|
|||||||
|
|
||||||
script:
|
script:
|
||||||
- phpunit -c phpunit.xml.dist
|
- phpunit -c phpunit.xml.dist
|
||||||
- phpcs --encoding=utf-8 --tab-width=4 --standard=framework/tests/phpcs -np framework
|
- phpcs --encoding=utf-8 --tab-width=4 --standard=framework/tests/phpcs/ruleset.xml -np framework
|
||||||
|
- phpcs --encoding=utf-8 --standard=framework/tests/phpcs/tabs.xml -np framework
|
||||||
|
|
||||||
branches:
|
branches:
|
||||||
except:
|
except:
|
||||||
|
@ -26,15 +26,15 @@ class CMSProfileController extends LeftAndMain {
|
|||||||
$form->Fields()->push(new HiddenField('ID', null, Member::currentUserID()));
|
$form->Fields()->push(new HiddenField('ID', null, Member::currentUserID()));
|
||||||
$form->Actions()->push(
|
$form->Actions()->push(
|
||||||
FormAction::create('save',_t('CMSMain.SAVE', 'Save'))
|
FormAction::create('save',_t('CMSMain.SAVE', 'Save'))
|
||||||
->addExtraClass('ss-ui-button ss-ui-action-constructive')
|
->addExtraClass('ss-ui-button ss-ui-action-constructive')
|
||||||
->setAttribute('data-icon', 'accept')
|
->setAttribute('data-icon', 'accept')
|
||||||
->setUseButtonTag(true)
|
->setUseButtonTag(true)
|
||||||
);
|
);
|
||||||
$form->Actions()->removeByName('delete');
|
$form->Actions()->removeByName('delete');
|
||||||
$form->setValidator(new Member_Validator());
|
$form->setValidator(new Member_Validator());
|
||||||
$form->setTemplate('Form');
|
$form->setTemplate('Form');
|
||||||
$form->setAttribute('data-pjax-fragment', null);
|
$form->setAttribute('data-pjax-fragment', null);
|
||||||
if($form->Fields()->hasTabset()) $form->Fields()->findOrMakeTab('Root')->setTemplate('CMSTabSet');
|
if($form->Fields()->hasTabset()) $form->Fields()->findOrMakeTab('Root')->setTemplate('CMSTabSet');
|
||||||
$form->addExtraClass('member-profile-form root-form cms-edit-form cms-panel-padded center');
|
$form->addExtraClass('member-profile-form root-form cms-edit-form cms-panel-padded center');
|
||||||
|
|
||||||
return $form;
|
return $form;
|
||||||
|
@ -296,7 +296,7 @@ abstract class ModelAdmin extends LeftAndMain {
|
|||||||
*
|
*
|
||||||
* @return array Map of model class names to importer instances
|
* @return array Map of model class names to importer instances
|
||||||
*/
|
*/
|
||||||
public function getModelImporters() {
|
public function getModelImporters() {
|
||||||
$importerClasses = $this->stat('model_importers');
|
$importerClasses = $this->stat('model_importers');
|
||||||
|
|
||||||
// fallback to all defined models if not explicitly defined
|
// fallback to all defined models if not explicitly defined
|
||||||
|
@ -153,8 +153,8 @@
|
|||||||
"select_limit" : 1,
|
"select_limit" : 1,
|
||||||
'initially_select': [this.find('.current').attr('id')]
|
'initially_select': [this.find('.current').attr('id')]
|
||||||
},
|
},
|
||||||
"crrm": {
|
"crrm": {
|
||||||
'move': {
|
'move': {
|
||||||
// Check if a node is allowed to be moved.
|
// Check if a node is allowed to be moved.
|
||||||
// Caution: Runs on every drag over a new node
|
// Caution: Runs on every drag over a new node
|
||||||
'check_move': function(data) {
|
'check_move': function(data) {
|
||||||
|
@ -301,7 +301,7 @@ jQuery.noConflict();
|
|||||||
*/
|
*/
|
||||||
submitForm: function(form, button, callback, ajaxOptions) {
|
submitForm: function(form, button, callback, ajaxOptions) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
// look for save button
|
// look for save button
|
||||||
if(!button) button = this.find('.Actions :submit[name=action_save]');
|
if(!button) button = this.find('.Actions :submit[name=action_save]');
|
||||||
// default to first button if none given - simulates browser behaviour
|
// default to first button if none given - simulates browser behaviour
|
||||||
|
@ -170,8 +170,8 @@
|
|||||||
convertUrlToDataUrl: function( absUrl ) {
|
convertUrlToDataUrl: function( absUrl ) {
|
||||||
var u = path.parseUrl( absUrl );
|
var u = path.parseUrl( absUrl );
|
||||||
if ( path.isEmbeddedPage( u ) ) {
|
if ( path.isEmbeddedPage( u ) ) {
|
||||||
// For embedded pages, remove the dialog hash key as in getFilePath(),
|
// For embedded pages, remove the dialog hash key as in getFilePath(),
|
||||||
// otherwise the Data Url won't match the id of the embedded Page.
|
// otherwise the Data Url won't match the id of the embedded Page.
|
||||||
return u.hash.split( dialogHashKey )[0].replace( /^#/, "" );
|
return u.hash.split( dialogHashKey )[0].replace( /^#/, "" );
|
||||||
} else if ( path.isSameDomain( u, documentBase ) ) {
|
} else if ( path.isSameDomain( u, documentBase ) ) {
|
||||||
return u.hrefNoHash.replace( documentBase.domain, "" );
|
return u.hrefNoHash.replace( documentBase.domain, "" );
|
||||||
@ -232,4 +232,4 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
$.path = path;
|
$.path = path;
|
||||||
}(jQuery));
|
}(jQuery));
|
||||||
|
@ -30,9 +30,9 @@ class FormEncodedDataFormatter extends XMLDataFormatter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function convertStringToArray($strData) {
|
public function convertStringToArray($strData) {
|
||||||
$postArray = array();
|
$postArray = array();
|
||||||
parse_str($strData, $postArray);
|
parse_str($strData, $postArray);
|
||||||
return $postArray;
|
return $postArray;
|
||||||
//TODO: It would be nice to implement this function in Convert.php
|
//TODO: It would be nice to implement this function in Convert.php
|
||||||
//return Convert::querystr2array($strData);
|
//return Convert::querystr2array($strData);
|
||||||
}
|
}
|
||||||
|
@ -106,9 +106,9 @@ class RSSFeed extends ViewableData {
|
|||||||
* every time the representation does
|
* every time the representation does
|
||||||
*/
|
*/
|
||||||
public function __construct(SS_List $entries, $link, $title,
|
public function __construct(SS_List $entries, $link, $title,
|
||||||
$description = null, $titleField = "Title",
|
$description = null, $titleField = "Title",
|
||||||
$descriptionField = "Content", $authorField = null,
|
$descriptionField = "Content", $authorField = null,
|
||||||
$lastModified = null, $etag = null) {
|
$lastModified = null, $etag = null) {
|
||||||
$this->entries = $entries;
|
$this->entries = $entries;
|
||||||
$this->link = $link;
|
$this->link = $link;
|
||||||
$this->description = $description;
|
$this->description = $description;
|
||||||
@ -269,7 +269,7 @@ class RSSFeed_Entry extends ViewableData {
|
|||||||
* Create a new RSSFeed entry.
|
* Create a new RSSFeed entry.
|
||||||
*/
|
*/
|
||||||
public function __construct($entry, $titleField, $descriptionField,
|
public function __construct($entry, $titleField, $descriptionField,
|
||||||
$authorField) {
|
$authorField) {
|
||||||
$this->failover = $entry;
|
$this->failover = $entry;
|
||||||
$this->titleField = $titleField;
|
$this->titleField = $titleField;
|
||||||
$this->descriptionField = $descriptionField;
|
$this->descriptionField = $descriptionField;
|
||||||
|
@ -65,7 +65,7 @@ class RestfulService extends ViewableData {
|
|||||||
* @param string $password The proxy auth password
|
* @param string $password The proxy auth password
|
||||||
* @param boolean $socks Set true to use socks5 proxy instead of http
|
* @param boolean $socks Set true to use socks5 proxy instead of http
|
||||||
*/
|
*/
|
||||||
public function setProxy($proxy, $port = 80, $user = "", $password = "", $socks = false) {
|
public function setProxy($proxy, $port = 80, $user = "", $password = "", $socks = false) {
|
||||||
$this->proxy = array(
|
$this->proxy = array(
|
||||||
CURLOPT_PROXY => $proxy,
|
CURLOPT_PROXY => $proxy,
|
||||||
CURLOPT_PROXYUSERPWD => "{$user}:{$password}",
|
CURLOPT_PROXYUSERPWD => "{$user}:{$password}",
|
||||||
@ -337,14 +337,14 @@ class RestfulService extends ViewableData {
|
|||||||
$child_count++;
|
$child_count++;
|
||||||
$k = ($parent == "") ? (string)$key : $parent . "_" . (string)$key;
|
$k = ($parent == "") ? (string)$key : $parent . "_" . (string)$key;
|
||||||
if($this->getRecurseValues($value,$data,$k) == 0){ // no childern, aka "leaf node"
|
if($this->getRecurseValues($value,$data,$k) == 0){ // no childern, aka "leaf node"
|
||||||
$conv_value = Convert::raw2xml($value);
|
$conv_value = Convert::raw2xml($value);
|
||||||
}
|
}
|
||||||
//Review the fix for similar node names overriding it's predecessor
|
//Review the fix for similar node names overriding it's predecessor
|
||||||
if(array_key_exists($k, $data) == true) {
|
if(array_key_exists($k, $data) == true) {
|
||||||
$data[$k] = $data[$k] . ",". $conv_value;
|
$data[$k] = $data[$k] . ",". $conv_value;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$data[$k] = $conv_value;
|
$data[$k] = $conv_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -37,19 +37,19 @@ chdir(dirname($_SERVER['SCRIPT_FILENAME']));
|
|||||||
* fourth => val
|
* fourth => val
|
||||||
*/
|
*/
|
||||||
if(isset($_SERVER['argv'][2])) {
|
if(isset($_SERVER['argv'][2])) {
|
||||||
$args = array_slice($_SERVER['argv'],2);
|
$args = array_slice($_SERVER['argv'],2);
|
||||||
if(!isset($_GET)) $_GET = array();
|
if(!isset($_GET)) $_GET = array();
|
||||||
if(!isset($_REQUEST)) $_REQUEST = array();
|
if(!isset($_REQUEST)) $_REQUEST = array();
|
||||||
foreach($args as $arg) {
|
foreach($args as $arg) {
|
||||||
if(strpos($arg,'=') == false) {
|
if(strpos($arg,'=') == false) {
|
||||||
$_GET['args'][] = $arg;
|
$_GET['args'][] = $arg;
|
||||||
} else {
|
} else {
|
||||||
$newItems = array();
|
$newItems = array();
|
||||||
parse_str( (substr($arg,0,2) == '--') ? substr($arg,2) : $arg, $newItems );
|
parse_str( (substr($arg,0,2) == '--') ? substr($arg,2) : $arg, $newItems );
|
||||||
$_GET = array_merge($_GET, $newItems);
|
$_GET = array_merge($_GET, $newItems);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$_REQUEST = array_merge($_REQUEST, $_GET);
|
$_REQUEST = array_merge($_REQUEST, $_GET);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set 'url' GET parameter
|
// Set 'url' GET parameter
|
||||||
@ -76,7 +76,7 @@ DB::connect($databaseConfig);
|
|||||||
$url = isset($_SERVER['argv'][1]) ? $_SERVER['argv'][1] : null;
|
$url = isset($_SERVER['argv'][1]) ? $_SERVER['argv'][1] : null;
|
||||||
if(!$url) {
|
if(!$url) {
|
||||||
echo 'Please specify an argument to cli-script.php/sake. For more information, visit'
|
echo 'Please specify an argument to cli-script.php/sake. For more information, visit'
|
||||||
. ' http://doc.silverstripe.org/framework/en/topics/commandline';
|
. ' http://doc.silverstripe.org/framework/en/topics/commandline';
|
||||||
die();
|
die();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,8 +100,8 @@ if(defined('SS_DATABASE_USERNAME') && defined('SS_DATABASE_PASSWORD')) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// For schema enabled drivers:
|
// For schema enabled drivers:
|
||||||
if(defined('SS_DATABASE_SCHEMA'))
|
if(defined('SS_DATABASE_SCHEMA'))
|
||||||
$databaseConfig["schema"] = SS_DATABASE_SCHEMA;
|
$databaseConfig["schema"] = SS_DATABASE_SCHEMA;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(defined('SS_SEND_ALL_EMAILS_TO')) {
|
if(defined('SS_SEND_ALL_EMAILS_TO')) {
|
||||||
|
@ -45,7 +45,7 @@ class ContentNegotiator {
|
|||||||
* that need to specify the character set make use of this function.
|
* that need to specify the character set make use of this function.
|
||||||
*/
|
*/
|
||||||
public static function get_encoding() {
|
public static function get_encoding() {
|
||||||
return self::$encoding;
|
return self::$encoding;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -96,7 +96,7 @@ class ContentNegotiator {
|
|||||||
} else {
|
} else {
|
||||||
// The W3C validator doesn't send an HTTP_ACCEPT header, but it can support xhtml. We put this special
|
// The W3C validator doesn't send an HTTP_ACCEPT header, but it can support xhtml. We put this special
|
||||||
// case in here so that designers don't get worried that their templates are HTML4.
|
// case in here so that designers don't get worried that their templates are HTML4.
|
||||||
if(isset($_SERVER['HTTP_USER_AGENT']) && substr($_SERVER['HTTP_USER_AGENT'], 0, 14) == 'W3C_Validator/') {
|
if(isset($_SERVER['HTTP_USER_AGENT']) && substr($_SERVER['HTTP_USER_AGENT'], 0, 14) == 'W3C_Validator/') {
|
||||||
$chosenFormat = "xhtml";
|
$chosenFormat = "xhtml";
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@ -164,7 +164,7 @@ class Controller extends RequestHandler implements TemplateGlobalProvider {
|
|||||||
Debug::message("Request handler $body->class object to $this->class controller;"
|
Debug::message("Request handler $body->class object to $this->class controller;"
|
||||||
. "rendering with template returned by $body->class::getViewer()");
|
. "rendering with template returned by $body->class::getViewer()");
|
||||||
}
|
}
|
||||||
$body = $body->getViewer($request->latestParam('Action'))->process($body);
|
$body = $body->getViewer($request->latestParam('Action'))->process($body);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->response->setBody($body);
|
$this->response->setBody($body);
|
||||||
@ -367,7 +367,7 @@ class Controller extends RequestHandler implements TemplateGlobalProvider {
|
|||||||
|
|
||||||
return $template->process($obj);
|
return $template->process($obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Call this to disable site-wide basic authentication for a specific contoller.
|
* Call this to disable site-wide basic authentication for a specific contoller.
|
||||||
* This must be called before Controller::init(). That is, you must call it in your controller's
|
* This must be called before Controller::init(). That is, you must call it in your controller's
|
||||||
|
@ -348,8 +348,8 @@ class Director implements TemplateGlobalProvider {
|
|||||||
$url = dirname($_SERVER['REQUEST_URI'] . 'x') . '/' . $url;
|
$url = dirname($_SERVER['REQUEST_URI'] . 'x') . '/' . $url;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(substr($url,0,4) != "http") {
|
if(substr($url,0,4) != "http") {
|
||||||
if($url[0] != "/") $url = Director::baseURL() . $url;
|
if($url[0] != "/") $url = Director::baseURL() . $url;
|
||||||
// Sometimes baseURL() can return a full URL instead of just a path
|
// Sometimes baseURL() can return a full URL instead of just a path
|
||||||
if(substr($url,0,4) != "http") $url = self::protocolAndHost() . $url;
|
if(substr($url,0,4) != "http") $url = self::protocolAndHost() . $url;
|
||||||
}
|
}
|
||||||
@ -630,21 +630,21 @@ class Director implements TemplateGlobalProvider {
|
|||||||
/**
|
/**
|
||||||
* Returns the Absolute URL of the site root.
|
* Returns the Absolute URL of the site root.
|
||||||
*/
|
*/
|
||||||
public static function absoluteBaseURL() {
|
public static function absoluteBaseURL() {
|
||||||
return Director::absoluteURL(Director::baseURL());
|
return Director::absoluteURL(Director::baseURL());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the Absolute URL of the site root, embedding the current basic-auth credentials into the URL.
|
* Returns the Absolute URL of the site root, embedding the current basic-auth credentials into the URL.
|
||||||
*/
|
*/
|
||||||
public static function absoluteBaseURLWithAuth() {
|
public static function absoluteBaseURLWithAuth() {
|
||||||
$s = "";
|
$s = "";
|
||||||
$login = "";
|
$login = "";
|
||||||
|
|
||||||
if(isset($_SERVER['PHP_AUTH_USER'])) $login = "$_SERVER[PHP_AUTH_USER]:$_SERVER[PHP_AUTH_PW]@";
|
if(isset($_SERVER['PHP_AUTH_USER'])) $login = "$_SERVER[PHP_AUTH_USER]:$_SERVER[PHP_AUTH_PW]@";
|
||||||
|
|
||||||
return Director::protocol() . $login . $_SERVER['HTTP_HOST'] . Director::baseURL();
|
return Director::protocol() . $login . $_SERVER['HTTP_HOST'] . Director::baseURL();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Force the site to run on SSL.
|
* Force the site to run on SSL.
|
||||||
@ -843,7 +843,7 @@ class Director implements TemplateGlobalProvider {
|
|||||||
$result = $_GET['isDev'];
|
$result = $_GET['isDev'];
|
||||||
} else {
|
} else {
|
||||||
if($firstTimeCheckingGetVar && DB::connection_attempted()) {
|
if($firstTimeCheckingGetVar && DB::connection_attempted()) {
|
||||||
echo "<p style=\"padding: 3px; margin: 3px; background-color: orange;
|
echo "<p style=\"padding: 3px; margin: 3px; background-color: orange;
|
||||||
color: white; font-weight: bold\">Sorry, you can't use ?isDev=1 until your
|
color: white; font-weight: bold\">Sorry, you can't use ?isDev=1 until your
|
||||||
Member and Group tables database are available. Perhaps your database
|
Member and Group tables database are available. Perhaps your database
|
||||||
connection is failing?</p>";
|
connection is failing?</p>";
|
||||||
|
@ -171,8 +171,8 @@ class HTTP {
|
|||||||
if($regexes) foreach($regexes as $regex) {
|
if($regexes) foreach($regexes as $regex) {
|
||||||
if(preg_match_all($regex, $content, $matches)) {
|
if(preg_match_all($regex, $content, $matches)) {
|
||||||
$result = array_merge_recursive($result, (isset($matches[2]) ? $matches[2] : $matches[1]));
|
$result = array_merge_recursive($result, (isset($matches[2]) ? $matches[2] : $matches[1]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return count($result) ? $result : null;
|
return count($result) ? $result : null;
|
||||||
}
|
}
|
||||||
|
@ -296,21 +296,21 @@ class SS_HTTPRequest implements ArrayAccess {
|
|||||||
public function getURL($includeGetVars = false) {
|
public function getURL($includeGetVars = false) {
|
||||||
$url = ($this->getExtension()) ? $this->url . '.' . $this->getExtension() : $this->url;
|
$url = ($this->getExtension()) ? $this->url . '.' . $this->getExtension() : $this->url;
|
||||||
|
|
||||||
if ($includeGetVars) {
|
if ($includeGetVars) {
|
||||||
// if we don't unset $vars['url'] we end up with /my/url?url=my/url&foo=bar etc
|
// if we don't unset $vars['url'] we end up with /my/url?url=my/url&foo=bar etc
|
||||||
|
|
||||||
$vars = $this->getVars();
|
$vars = $this->getVars();
|
||||||
unset($vars['url']);
|
unset($vars['url']);
|
||||||
|
|
||||||
if (count($vars)) {
|
if (count($vars)) {
|
||||||
$url .= '?' . http_build_query($vars);
|
$url .= '?' . http_build_query($vars);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(strpos($url, "?") !== false) {
|
else if(strpos($url, "?") !== false) {
|
||||||
$url = substr($url, 0, strpos($url, "?"));
|
$url = substr($url, 0, strpos($url, "?"));
|
||||||
}
|
}
|
||||||
|
|
||||||
return $url;
|
return $url;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -501,9 +501,9 @@ class SS_HTTPRequest implements ArrayAccess {
|
|||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function shiftAllParams() {
|
public function shiftAllParams() {
|
||||||
$keys = array_keys($this->allParams);
|
$keys = array_keys($this->allParams);
|
||||||
$values = array_values($this->allParams);
|
$values = array_values($this->allParams);
|
||||||
$value = array_shift($values);
|
$value = array_shift($values);
|
||||||
|
|
||||||
// push additional unparsed URL parts onto the parameter stack
|
// push additional unparsed URL parts onto the parameter stack
|
||||||
if(array_key_exists($this->unshiftedButParsedParts, $this->dirParts)) {
|
if(array_key_exists($this->unshiftedButParsedParts, $this->dirParts)) {
|
||||||
@ -636,10 +636,10 @@ class SS_HTTPRequest implements ArrayAccess {
|
|||||||
*/
|
*/
|
||||||
public function getIP() {
|
public function getIP() {
|
||||||
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
|
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
|
||||||
//check ip from share internet
|
//check ip from share internet
|
||||||
return $_SERVER['HTTP_CLIENT_IP'];
|
return $_SERVER['HTTP_CLIENT_IP'];
|
||||||
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
|
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
|
||||||
//to check ip is pass from proxy
|
//to check ip is pass from proxy
|
||||||
return $_SERVER['HTTP_X_FORWARDED_FOR'];
|
return $_SERVER['HTTP_X_FORWARDED_FOR'];
|
||||||
} elseif(isset($_SERVER['REMOTE_ADDR'])) {
|
} elseif(isset($_SERVER['REMOTE_ADDR'])) {
|
||||||
return $_SERVER['REMOTE_ADDR'];
|
return $_SERVER['REMOTE_ADDR'];
|
||||||
@ -655,12 +655,12 @@ class SS_HTTPRequest implements ArrayAccess {
|
|||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function getAcceptMimetypes($includeQuality = false) {
|
public function getAcceptMimetypes($includeQuality = false) {
|
||||||
$mimetypes = array();
|
$mimetypes = array();
|
||||||
$mimetypesWithQuality = explode(',',$this->getHeader('Accept'));
|
$mimetypesWithQuality = explode(',',$this->getHeader('Accept'));
|
||||||
foreach($mimetypesWithQuality as $mimetypeWithQuality) {
|
foreach($mimetypesWithQuality as $mimetypeWithQuality) {
|
||||||
$mimetypes[] = ($includeQuality) ? $mimetypeWithQuality : preg_replace('/;.*/', '', $mimetypeWithQuality);
|
$mimetypes[] = ($includeQuality) ? $mimetypeWithQuality : preg_replace('/;.*/', '', $mimetypeWithQuality);
|
||||||
}
|
}
|
||||||
return $mimetypes;
|
return $mimetypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -303,37 +303,37 @@ class SS_HTTPResponse_Exception extends Exception {
|
|||||||
* $statusDescription will be the HTTP status of the resulting response.
|
* $statusDescription will be the HTTP status of the resulting response.
|
||||||
* @see SS_HTTPResponse::__construct();
|
* @see SS_HTTPResponse::__construct();
|
||||||
*/
|
*/
|
||||||
public function __construct($body = null, $statusCode = null, $statusDescription = null) {
|
public function __construct($body = null, $statusCode = null, $statusDescription = null) {
|
||||||
if($body instanceof SS_HTTPResponse) {
|
if($body instanceof SS_HTTPResponse) {
|
||||||
// statusCode and statusDescription should override whatever is passed in the body
|
// statusCode and statusDescription should override whatever is passed in the body
|
||||||
if($statusCode) $body->setStatusCode($statusCode);
|
if($statusCode) $body->setStatusCode($statusCode);
|
||||||
if($statusDescription) $body->setStatusDescription($statusDescription);
|
if($statusDescription) $body->setStatusDescription($statusDescription);
|
||||||
|
|
||||||
$this->setResponse($body);
|
$this->setResponse($body);
|
||||||
} else {
|
} else {
|
||||||
$response = new SS_HTTPResponse($body, $statusCode, $statusDescription);
|
$response = new SS_HTTPResponse($body, $statusCode, $statusDescription);
|
||||||
|
|
||||||
// Error responses should always be considered plaintext, for security reasons
|
// Error responses should always be considered plaintext, for security reasons
|
||||||
$response->addHeader('Content-Type', 'text/plain');
|
$response->addHeader('Content-Type', 'text/plain');
|
||||||
|
|
||||||
$this->setResponse($response);
|
$this->setResponse($response);
|
||||||
}
|
}
|
||||||
|
|
||||||
parent::__construct($this->getResponse()->getBody(), $this->getResponse()->getStatusCode());
|
parent::__construct($this->getResponse()->getBody(), $this->getResponse()->getStatusCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return SS_HTTPResponse
|
* @return SS_HTTPResponse
|
||||||
*/
|
*/
|
||||||
public function getResponse() {
|
public function getResponse() {
|
||||||
return $this->response;
|
return $this->response;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param SS_HTTPResponse $response
|
* @param SS_HTTPResponse $response
|
||||||
*/
|
*/
|
||||||
public function setResponse(SS_HTTPResponse $response) {
|
public function setResponse(SS_HTTPResponse $response) {
|
||||||
$this->response = $response;
|
$this->response = $response;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -94,7 +94,7 @@ class RequestHandler extends ViewableData {
|
|||||||
* or by defining $allowed_actions in your {@link Form} class.
|
* or by defining $allowed_actions in your {@link Form} class.
|
||||||
*/
|
*/
|
||||||
static $allowed_actions = null;
|
static $allowed_actions = null;
|
||||||
|
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
$this->brokenOnConstruct = false;
|
$this->brokenOnConstruct = false;
|
||||||
|
|
||||||
|
@ -36,6 +36,6 @@ class AopProxyService {
|
|||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -69,18 +69,18 @@ class ArrayLib {
|
|||||||
* @todo Improve documentation
|
* @todo Improve documentation
|
||||||
*/
|
*/
|
||||||
public static function array_values_recursive($arr) {
|
public static function array_values_recursive($arr) {
|
||||||
$lst = array();
|
$lst = array();
|
||||||
foreach(array_keys($arr) as $k){
|
foreach(array_keys($arr) as $k){
|
||||||
$v = $arr[$k];
|
$v = $arr[$k];
|
||||||
if (is_scalar($v)) {
|
if (is_scalar($v)) {
|
||||||
$lst[] = $v;
|
$lst[] = $v;
|
||||||
} elseif (is_array($v)) {
|
} elseif (is_array($v)) {
|
||||||
$lst = array_merge( $lst,
|
$lst = array_merge( $lst,
|
||||||
self::array_values_recursive($v)
|
self::array_values_recursive($v)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return $lst;
|
return $lst;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -110,12 +110,12 @@ class ArrayLib {
|
|||||||
*/
|
*/
|
||||||
public static function is_associative($arr) {
|
public static function is_associative($arr) {
|
||||||
if(is_array($arr) && ! empty($arr)) {
|
if(is_array($arr) && ! empty($arr)) {
|
||||||
for($iterator = count($arr) - 1; $iterator; $iterator--) {
|
for($iterator = count($arr) - 1; $iterator; $iterator--) {
|
||||||
if (!array_key_exists($iterator, $arr)) return true;
|
if (!array_key_exists($iterator, $arr)) return true;
|
||||||
}
|
}
|
||||||
return !array_key_exists(0, $arr);
|
return !array_key_exists(0, $arr);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
872
core/Diff.php
872
core/Diff.php
@ -27,21 +27,21 @@ define('USE_ASSERTS', function_exists('assert'));
|
|||||||
* @access private
|
* @access private
|
||||||
*/
|
*/
|
||||||
class _DiffOp {
|
class _DiffOp {
|
||||||
var $type;
|
var $type;
|
||||||
var $orig;
|
var $orig;
|
||||||
var $final;
|
var $final;
|
||||||
|
|
||||||
public function reverse() {
|
public function reverse() {
|
||||||
trigger_error("pure virtual", E_USER_ERROR);
|
trigger_error("pure virtual", E_USER_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function norig() {
|
public function norig() {
|
||||||
return $this->orig ? sizeof($this->orig) : 0;
|
return $this->orig ? sizeof($this->orig) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function nfinal() {
|
public function nfinal() {
|
||||||
return $this->final ? sizeof($this->final) : 0;
|
return $this->final ? sizeof($this->final) : 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -50,18 +50,18 @@ class _DiffOp {
|
|||||||
* @access private
|
* @access private
|
||||||
*/
|
*/
|
||||||
class _DiffOp_Copy extends _DiffOp {
|
class _DiffOp_Copy extends _DiffOp {
|
||||||
var $type = 'copy';
|
var $type = 'copy';
|
||||||
|
|
||||||
public function _DiffOp_Copy ($orig, $final = false) {
|
public function _DiffOp_Copy ($orig, $final = false) {
|
||||||
if (!is_array($final))
|
if (!is_array($final))
|
||||||
$final = $orig;
|
$final = $orig;
|
||||||
$this->orig = $orig;
|
$this->orig = $orig;
|
||||||
$this->final = $final;
|
$this->final = $final;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function reverse() {
|
public function reverse() {
|
||||||
return new _DiffOp_Copy($this->final, $this->orig);
|
return new _DiffOp_Copy($this->final, $this->orig);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -70,16 +70,16 @@ class _DiffOp_Copy extends _DiffOp {
|
|||||||
* @access private
|
* @access private
|
||||||
*/
|
*/
|
||||||
class _DiffOp_Delete extends _DiffOp {
|
class _DiffOp_Delete extends _DiffOp {
|
||||||
var $type = 'delete';
|
var $type = 'delete';
|
||||||
|
|
||||||
public function _DiffOp_Delete ($lines) {
|
public function _DiffOp_Delete ($lines) {
|
||||||
$this->orig = $lines;
|
$this->orig = $lines;
|
||||||
$this->final = false;
|
$this->final = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function reverse() {
|
public function reverse() {
|
||||||
return new _DiffOp_Add($this->orig);
|
return new _DiffOp_Add($this->orig);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -88,16 +88,16 @@ class _DiffOp_Delete extends _DiffOp {
|
|||||||
* @access private
|
* @access private
|
||||||
*/
|
*/
|
||||||
class _DiffOp_Add extends _DiffOp {
|
class _DiffOp_Add extends _DiffOp {
|
||||||
var $type = 'add';
|
var $type = 'add';
|
||||||
|
|
||||||
public function _DiffOp_Add ($lines) {
|
public function _DiffOp_Add ($lines) {
|
||||||
$this->final = $lines;
|
$this->final = $lines;
|
||||||
$this->orig = false;
|
$this->orig = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function reverse() {
|
public function reverse() {
|
||||||
return new _DiffOp_Delete($this->final);
|
return new _DiffOp_Delete($this->final);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -106,16 +106,16 @@ class _DiffOp_Add extends _DiffOp {
|
|||||||
* @access private
|
* @access private
|
||||||
*/
|
*/
|
||||||
class _DiffOp_Change extends _DiffOp {
|
class _DiffOp_Change extends _DiffOp {
|
||||||
var $type = 'change';
|
var $type = 'change';
|
||||||
|
|
||||||
public function _DiffOp_Change ($orig, $final) {
|
public function _DiffOp_Change ($orig, $final) {
|
||||||
$this->orig = $orig;
|
$this->orig = $orig;
|
||||||
$this->final = $final;
|
$this->final = $final;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function reverse() {
|
public function reverse() {
|
||||||
return new _DiffOp_Change($this->final, $this->orig);
|
return new _DiffOp_Change($this->final, $this->orig);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -143,126 +143,126 @@ class _DiffOp_Change extends _DiffOp {
|
|||||||
*/
|
*/
|
||||||
class _DiffEngine
|
class _DiffEngine
|
||||||
{
|
{
|
||||||
public function diff ($from_lines, $to_lines) {
|
public function diff ($from_lines, $to_lines) {
|
||||||
$n_from = sizeof($from_lines);
|
$n_from = sizeof($from_lines);
|
||||||
$n_to = sizeof($to_lines);
|
$n_to = sizeof($to_lines);
|
||||||
|
|
||||||
$this->xchanged = $this->ychanged = array();
|
$this->xchanged = $this->ychanged = array();
|
||||||
$this->xv = $this->yv = array();
|
$this->xv = $this->yv = array();
|
||||||
$this->xind = $this->yind = array();
|
$this->xind = $this->yind = array();
|
||||||
unset($this->seq);
|
unset($this->seq);
|
||||||
unset($this->in_seq);
|
unset($this->in_seq);
|
||||||
unset($this->lcs);
|
unset($this->lcs);
|
||||||
|
|
||||||
// Skip leading common lines.
|
// Skip leading common lines.
|
||||||
for ($skip = 0; $skip < $n_from && $skip < $n_to; $skip++) {
|
for ($skip = 0; $skip < $n_from && $skip < $n_to; $skip++) {
|
||||||
if ($from_lines[$skip] != $to_lines[$skip])
|
if ($from_lines[$skip] != $to_lines[$skip])
|
||||||
break;
|
break;
|
||||||
$this->xchanged[$skip] = $this->ychanged[$skip] = false;
|
$this->xchanged[$skip] = $this->ychanged[$skip] = false;
|
||||||
}
|
}
|
||||||
// Skip trailing common lines.
|
// Skip trailing common lines.
|
||||||
$xi = $n_from; $yi = $n_to;
|
$xi = $n_from; $yi = $n_to;
|
||||||
for ($endskip = 0; --$xi > $skip && --$yi > $skip; $endskip++) {
|
for ($endskip = 0; --$xi > $skip && --$yi > $skip; $endskip++) {
|
||||||
if ($from_lines[$xi] != $to_lines[$yi])
|
if ($from_lines[$xi] != $to_lines[$yi])
|
||||||
break;
|
break;
|
||||||
$this->xchanged[$xi] = $this->ychanged[$yi] = false;
|
$this->xchanged[$xi] = $this->ychanged[$yi] = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ignore lines which do not exist in both files.
|
// Ignore lines which do not exist in both files.
|
||||||
for ($xi = $skip; $xi < $n_from - $endskip; $xi++)
|
for ($xi = $skip; $xi < $n_from - $endskip; $xi++)
|
||||||
$xhash[$from_lines[$xi]] = 1;
|
$xhash[$from_lines[$xi]] = 1;
|
||||||
for ($yi = $skip; $yi < $n_to - $endskip; $yi++) {
|
for ($yi = $skip; $yi < $n_to - $endskip; $yi++) {
|
||||||
$line = $to_lines[$yi];
|
$line = $to_lines[$yi];
|
||||||
if ( ($this->ychanged[$yi] = empty($xhash[$line])) )
|
if ( ($this->ychanged[$yi] = empty($xhash[$line])) )
|
||||||
continue;
|
continue;
|
||||||
$yhash[$line] = 1;
|
$yhash[$line] = 1;
|
||||||
$this->yv[] = $line;
|
$this->yv[] = $line;
|
||||||
$this->yind[] = $yi;
|
$this->yind[] = $yi;
|
||||||
}
|
}
|
||||||
for ($xi = $skip; $xi < $n_from - $endskip; $xi++) {
|
for ($xi = $skip; $xi < $n_from - $endskip; $xi++) {
|
||||||
$line = $from_lines[$xi];
|
$line = $from_lines[$xi];
|
||||||
if ( ($this->xchanged[$xi] = empty($yhash[$line])) )
|
if ( ($this->xchanged[$xi] = empty($yhash[$line])) )
|
||||||
continue;
|
continue;
|
||||||
$this->xv[] = $line;
|
$this->xv[] = $line;
|
||||||
$this->xind[] = $xi;
|
$this->xind[] = $xi;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the LCS.
|
// Find the LCS.
|
||||||
$this->_compareseq(0, sizeof($this->xv), 0, sizeof($this->yv));
|
$this->_compareseq(0, sizeof($this->xv), 0, sizeof($this->yv));
|
||||||
|
|
||||||
// Merge edits when possible
|
// Merge edits when possible
|
||||||
$this->_shift_boundaries($from_lines, $this->xchanged, $this->ychanged);
|
$this->_shift_boundaries($from_lines, $this->xchanged, $this->ychanged);
|
||||||
$this->_shift_boundaries($to_lines, $this->ychanged, $this->xchanged);
|
$this->_shift_boundaries($to_lines, $this->ychanged, $this->xchanged);
|
||||||
|
|
||||||
// Compute the edit operations.
|
// Compute the edit operations.
|
||||||
$edits = array();
|
$edits = array();
|
||||||
$xi = $yi = 0;
|
$xi = $yi = 0;
|
||||||
while ($xi < $n_from || $yi < $n_to) {
|
while ($xi < $n_from || $yi < $n_to) {
|
||||||
USE_ASSERTS && assert($yi < $n_to || $this->xchanged[$xi]);
|
USE_ASSERTS && assert($yi < $n_to || $this->xchanged[$xi]);
|
||||||
USE_ASSERTS && assert($xi < $n_from || $this->ychanged[$yi]);
|
USE_ASSERTS && assert($xi < $n_from || $this->ychanged[$yi]);
|
||||||
|
|
||||||
// Skip matching "snake".
|
// Skip matching "snake".
|
||||||
$copy = array();
|
$copy = array();
|
||||||
while ( $xi < $n_from && $yi < $n_to
|
while ( $xi < $n_from && $yi < $n_to
|
||||||
&& !$this->xchanged[$xi] && !$this->ychanged[$yi]) {
|
&& !$this->xchanged[$xi] && !$this->ychanged[$yi]) {
|
||||||
$copy[] = $from_lines[$xi++];
|
$copy[] = $from_lines[$xi++];
|
||||||
++$yi;
|
++$yi;
|
||||||
}
|
}
|
||||||
if ($copy)
|
if ($copy)
|
||||||
$edits[] = new _DiffOp_Copy($copy);
|
$edits[] = new _DiffOp_Copy($copy);
|
||||||
|
|
||||||
// Find deletes & adds.
|
// Find deletes & adds.
|
||||||
$delete = array();
|
$delete = array();
|
||||||
while ($xi < $n_from && $this->xchanged[$xi])
|
while ($xi < $n_from && $this->xchanged[$xi])
|
||||||
$delete[] = $from_lines[$xi++];
|
$delete[] = $from_lines[$xi++];
|
||||||
|
|
||||||
$add = array();
|
$add = array();
|
||||||
while ($yi < $n_to && $this->ychanged[$yi])
|
while ($yi < $n_to && $this->ychanged[$yi])
|
||||||
$add[] = $to_lines[$yi++];
|
$add[] = $to_lines[$yi++];
|
||||||
|
|
||||||
if ($delete && $add)
|
if ($delete && $add)
|
||||||
$edits[] = new _DiffOp_Change($delete, $add);
|
$edits[] = new _DiffOp_Change($delete, $add);
|
||||||
elseif ($delete)
|
elseif ($delete)
|
||||||
$edits[] = new _DiffOp_Delete($delete);
|
$edits[] = new _DiffOp_Delete($delete);
|
||||||
elseif ($add)
|
elseif ($add)
|
||||||
$edits[] = new _DiffOp_Add($add);
|
$edits[] = new _DiffOp_Add($add);
|
||||||
}
|
}
|
||||||
return $edits;
|
return $edits;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Divide the Largest Common Subsequence (LCS) of the sequences
|
/* Divide the Largest Common Subsequence (LCS) of the sequences
|
||||||
* [XOFF, XLIM) and [YOFF, YLIM) into NCHUNKS approximately equally
|
* [XOFF, XLIM) and [YOFF, YLIM) into NCHUNKS approximately equally
|
||||||
* sized segments.
|
* sized segments.
|
||||||
*
|
*
|
||||||
* Returns (LCS, PTS). LCS is the length of the LCS. PTS is an
|
* Returns (LCS, PTS). LCS is the length of the LCS. PTS is an
|
||||||
* array of NCHUNKS+1 (X, Y) indexes giving the diving points between
|
* array of NCHUNKS+1 (X, Y) indexes giving the diving points between
|
||||||
* sub sequences. The first sub-sequence is contained in [X0, X1),
|
* sub sequences. The first sub-sequence is contained in [X0, X1),
|
||||||
* [Y0, Y1), the second in [X1, X2), [Y1, Y2) and so on. Note
|
* [Y0, Y1), the second in [X1, X2), [Y1, Y2) and so on. Note
|
||||||
* that (X0, Y0) == (XOFF, YOFF) and
|
* that (X0, Y0) == (XOFF, YOFF) and
|
||||||
* (X[NCHUNKS], Y[NCHUNKS]) == (XLIM, YLIM).
|
* (X[NCHUNKS], Y[NCHUNKS]) == (XLIM, YLIM).
|
||||||
*
|
*
|
||||||
* This function assumes that the first lines of the specified portions
|
* This function assumes that the first lines of the specified portions
|
||||||
* of the two files do not match, and likewise that the last lines do not
|
* of the two files do not match, and likewise that the last lines do not
|
||||||
* match. The caller must trim matching lines from the beginning and end
|
* match. The caller must trim matching lines from the beginning and end
|
||||||
* of the portions it is going to specify.
|
* of the portions it is going to specify.
|
||||||
*/
|
*/
|
||||||
public function _diag ($xoff, $xlim, $yoff, $ylim, $nchunks) {
|
public function _diag ($xoff, $xlim, $yoff, $ylim, $nchunks) {
|
||||||
$flip = false;
|
$flip = false;
|
||||||
|
|
||||||
if ($xlim - $xoff > $ylim - $yoff) {
|
if ($xlim - $xoff > $ylim - $yoff) {
|
||||||
// Things seems faster (I'm not sure I understand why)
|
// Things seems faster (I'm not sure I understand why)
|
||||||
// when the shortest sequence in X.
|
// when the shortest sequence in X.
|
||||||
$flip = true;
|
$flip = true;
|
||||||
list ($xoff, $xlim, $yoff, $ylim)
|
list ($xoff, $xlim, $yoff, $ylim)
|
||||||
= array( $yoff, $ylim, $xoff, $xlim);
|
= array( $yoff, $ylim, $xoff, $xlim);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($flip)
|
if ($flip)
|
||||||
for ($i = $ylim - 1; $i >= $yoff; $i--)
|
for ($i = $ylim - 1; $i >= $yoff; $i--)
|
||||||
$ymatches[$this->xv[$i]][] = $i;
|
$ymatches[$this->xv[$i]][] = $i;
|
||||||
else
|
else
|
||||||
for ($i = $ylim - 1; $i >= $yoff; $i--)
|
for ($i = $ylim - 1; $i >= $yoff; $i--)
|
||||||
$ymatches[$this->yv[$i]][] = $i;
|
$ymatches[$this->yv[$i]][] = $i;
|
||||||
|
|
||||||
$this->lcs = 0;
|
$this->lcs = 0;
|
||||||
@ -273,70 +273,70 @@ class _DiffEngine
|
|||||||
$numer = $xlim - $xoff + $nchunks - 1;
|
$numer = $xlim - $xoff + $nchunks - 1;
|
||||||
$x = $xoff;
|
$x = $xoff;
|
||||||
for ($chunk = 0; $chunk < $nchunks; $chunk++) {
|
for ($chunk = 0; $chunk < $nchunks; $chunk++) {
|
||||||
if ($chunk > 0)
|
if ($chunk > 0)
|
||||||
for ($i = 0; $i <= $this->lcs; $i++)
|
for ($i = 0; $i <= $this->lcs; $i++)
|
||||||
$ymids[$i][$chunk-1] = $this->seq[$i];
|
$ymids[$i][$chunk-1] = $this->seq[$i];
|
||||||
|
|
||||||
$x1 = $xoff + (int)(($numer + ($xlim-$xoff)*$chunk) / $nchunks);
|
$x1 = $xoff + (int)(($numer + ($xlim-$xoff)*$chunk) / $nchunks);
|
||||||
for ( ; $x < $x1; $x++) {
|
for ( ; $x < $x1; $x++) {
|
||||||
$line = $flip ? $this->yv[$x] : $this->xv[$x];
|
$line = $flip ? $this->yv[$x] : $this->xv[$x];
|
||||||
if (empty($ymatches[$line]))
|
if (empty($ymatches[$line]))
|
||||||
continue;
|
continue;
|
||||||
$matches = $ymatches[$line];
|
$matches = $ymatches[$line];
|
||||||
reset($matches);
|
reset($matches);
|
||||||
while (list ($junk, $y) = each($matches))
|
while (list ($junk, $y) = each($matches))
|
||||||
if (empty($this->in_seq[$y])) {
|
if (empty($this->in_seq[$y])) {
|
||||||
$k = $this->_lcs_pos($y);
|
$k = $this->_lcs_pos($y);
|
||||||
USE_ASSERTS && assert($k > 0);
|
USE_ASSERTS && assert($k > 0);
|
||||||
$ymids[$k] = $ymids[$k-1];
|
$ymids[$k] = $ymids[$k-1];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
while (list ($junk, $y) = each($matches)) {
|
while (list ($junk, $y) = each($matches)) {
|
||||||
if ($y > $this->seq[$k-1]) {
|
if ($y > $this->seq[$k-1]) {
|
||||||
USE_ASSERTS && assert($y < $this->seq[$k]);
|
USE_ASSERTS && assert($y < $this->seq[$k]);
|
||||||
// Optimization: this is a common case:
|
// Optimization: this is a common case:
|
||||||
// next match is just replacing previous match.
|
// next match is just replacing previous match.
|
||||||
$this->in_seq[$this->seq[$k]] = false;
|
$this->in_seq[$this->seq[$k]] = false;
|
||||||
$this->seq[$k] = $y;
|
$this->seq[$k] = $y;
|
||||||
$this->in_seq[$y] = 1;
|
$this->in_seq[$y] = 1;
|
||||||
}
|
}
|
||||||
else if (empty($this->in_seq[$y])) {
|
else if (empty($this->in_seq[$y])) {
|
||||||
$k = $this->_lcs_pos($y);
|
$k = $this->_lcs_pos($y);
|
||||||
USE_ASSERTS && assert($k > 0);
|
USE_ASSERTS && assert($k > 0);
|
||||||
$ymids[$k] = $ymids[$k-1];
|
$ymids[$k] = $ymids[$k-1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$seps[] = $flip ? array($yoff, $xoff) : array($xoff, $yoff);
|
$seps[] = $flip ? array($yoff, $xoff) : array($xoff, $yoff);
|
||||||
$ymid = $ymids[$this->lcs];
|
$ymid = $ymids[$this->lcs];
|
||||||
for ($n = 0; $n < $nchunks - 1; $n++) {
|
for ($n = 0; $n < $nchunks - 1; $n++) {
|
||||||
$x1 = $xoff + (int)(($numer + ($xlim - $xoff) * $n) / $nchunks);
|
$x1 = $xoff + (int)(($numer + ($xlim - $xoff) * $n) / $nchunks);
|
||||||
$y1 = $ymid[$n] + 1;
|
$y1 = $ymid[$n] + 1;
|
||||||
$seps[] = $flip ? array($y1, $x1) : array($x1, $y1);
|
$seps[] = $flip ? array($y1, $x1) : array($x1, $y1);
|
||||||
}
|
}
|
||||||
$seps[] = $flip ? array($ylim, $xlim) : array($xlim, $ylim);
|
$seps[] = $flip ? array($ylim, $xlim) : array($xlim, $ylim);
|
||||||
|
|
||||||
return array($this->lcs, $seps);
|
return array($this->lcs, $seps);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function _lcs_pos ($ypos) {
|
public function _lcs_pos ($ypos) {
|
||||||
$end = $this->lcs;
|
$end = $this->lcs;
|
||||||
if ($end == 0 || $ypos > $this->seq[$end]) {
|
if ($end == 0 || $ypos > $this->seq[$end]) {
|
||||||
$this->seq[++$this->lcs] = $ypos;
|
$this->seq[++$this->lcs] = $ypos;
|
||||||
$this->in_seq[$ypos] = 1;
|
$this->in_seq[$ypos] = 1;
|
||||||
return $this->lcs;
|
return $this->lcs;
|
||||||
}
|
}
|
||||||
|
|
||||||
$beg = 1;
|
$beg = 1;
|
||||||
while ($beg < $end) {
|
while ($beg < $end) {
|
||||||
$mid = (int)(($beg + $end) / 2);
|
$mid = (int)(($beg + $end) / 2);
|
||||||
if ( $ypos > $this->seq[$mid] )
|
if ( $ypos > $this->seq[$mid] )
|
||||||
$beg = $mid + 1;
|
$beg = $mid + 1;
|
||||||
else
|
else
|
||||||
$end = $mid;
|
$end = $mid;
|
||||||
}
|
}
|
||||||
|
|
||||||
USE_ASSERTS && assert($ypos != $this->seq[$end]);
|
USE_ASSERTS && assert($ypos != $this->seq[$end]);
|
||||||
|
|
||||||
@ -344,77 +344,77 @@ class _DiffEngine
|
|||||||
$this->seq[$end] = $ypos;
|
$this->seq[$end] = $ypos;
|
||||||
$this->in_seq[$ypos] = 1;
|
$this->in_seq[$ypos] = 1;
|
||||||
return $end;
|
return $end;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Find LCS of two sequences.
|
/* Find LCS of two sequences.
|
||||||
*
|
*
|
||||||
* The results are recorded in the vectors $this->{x,y}changed[], by
|
* The results are recorded in the vectors $this->{x,y}changed[], by
|
||||||
* storing a 1 in the element for each line that is an insertion
|
* storing a 1 in the element for each line that is an insertion
|
||||||
* or deletion (ie. is not in the LCS).
|
* or deletion (ie. is not in the LCS).
|
||||||
*
|
*
|
||||||
* The subsequence of file 0 is [XOFF, XLIM) and likewise for file 1.
|
* The subsequence of file 0 is [XOFF, XLIM) and likewise for file 1.
|
||||||
*
|
*
|
||||||
* Note that XLIM, YLIM are exclusive bounds.
|
* Note that XLIM, YLIM are exclusive bounds.
|
||||||
* All line numbers are origin-0 and discarded lines are not counted.
|
* All line numbers are origin-0 and discarded lines are not counted.
|
||||||
*/
|
*/
|
||||||
public function _compareseq ($xoff, $xlim, $yoff, $ylim) {
|
public function _compareseq ($xoff, $xlim, $yoff, $ylim) {
|
||||||
// Slide down the bottom initial diagonal.
|
// Slide down the bottom initial diagonal.
|
||||||
while ($xoff < $xlim && $yoff < $ylim
|
while ($xoff < $xlim && $yoff < $ylim
|
||||||
&& $this->xv[$xoff] == $this->yv[$yoff]) {
|
&& $this->xv[$xoff] == $this->yv[$yoff]) {
|
||||||
++$xoff;
|
++$xoff;
|
||||||
++$yoff;
|
++$yoff;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Slide up the top initial diagonal.
|
// Slide up the top initial diagonal.
|
||||||
while ($xlim > $xoff && $ylim > $yoff
|
while ($xlim > $xoff && $ylim > $yoff
|
||||||
&& $this->xv[$xlim - 1] == $this->yv[$ylim - 1]) {
|
&& $this->xv[$xlim - 1] == $this->yv[$ylim - 1]) {
|
||||||
--$xlim;
|
--$xlim;
|
||||||
--$ylim;
|
--$ylim;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($xoff == $xlim || $yoff == $ylim)
|
if ($xoff == $xlim || $yoff == $ylim)
|
||||||
$lcs = 0;
|
$lcs = 0;
|
||||||
else {
|
else {
|
||||||
// This is ad hoc but seems to work well.
|
// This is ad hoc but seems to work well.
|
||||||
//$nchunks = sqrt(min($xlim - $xoff, $ylim - $yoff) / 2.5);
|
//$nchunks = sqrt(min($xlim - $xoff, $ylim - $yoff) / 2.5);
|
||||||
//$nchunks = max(2,min(8,(int)$nchunks));
|
//$nchunks = max(2,min(8,(int)$nchunks));
|
||||||
$nchunks = min(7, $xlim - $xoff, $ylim - $yoff) + 1;
|
$nchunks = min(7, $xlim - $xoff, $ylim - $yoff) + 1;
|
||||||
list ($lcs, $seps)
|
list ($lcs, $seps)
|
||||||
= $this->_diag($xoff,$xlim,$yoff, $ylim,$nchunks);
|
= $this->_diag($xoff,$xlim,$yoff, $ylim,$nchunks);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($lcs == 0) {
|
if ($lcs == 0) {
|
||||||
// X and Y sequences have no common subsequence:
|
// X and Y sequences have no common subsequence:
|
||||||
// mark all changed.
|
// mark all changed.
|
||||||
while ($yoff < $ylim)
|
while ($yoff < $ylim)
|
||||||
$this->ychanged[$this->yind[$yoff++]] = 1;
|
$this->ychanged[$this->yind[$yoff++]] = 1;
|
||||||
while ($xoff < $xlim)
|
while ($xoff < $xlim)
|
||||||
$this->xchanged[$this->xind[$xoff++]] = 1;
|
$this->xchanged[$this->xind[$xoff++]] = 1;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Use the partitions to split this problem into subproblems.
|
// Use the partitions to split this problem into subproblems.
|
||||||
reset($seps);
|
reset($seps);
|
||||||
$pt1 = $seps[0];
|
$pt1 = $seps[0];
|
||||||
while ($pt2 = next($seps)) {
|
while ($pt2 = next($seps)) {
|
||||||
$this->_compareseq ($pt1[0], $pt2[0], $pt1[1], $pt2[1]);
|
$this->_compareseq ($pt1[0], $pt2[0], $pt1[1], $pt2[1]);
|
||||||
$pt1 = $pt2;
|
$pt1 = $pt2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Adjust inserts/deletes of identical lines to join changes
|
/* Adjust inserts/deletes of identical lines to join changes
|
||||||
* as much as possible.
|
* as much as possible.
|
||||||
*
|
*
|
||||||
* We do something when a run of changed lines include a
|
* We do something when a run of changed lines include a
|
||||||
* line at one end and has an excluded, identical line at the other.
|
* line at one end and has an excluded, identical line at the other.
|
||||||
* We are free to choose which identical line is included.
|
* We are free to choose which identical line is included.
|
||||||
* 'compareseq' usually chooses the one at the beginning,
|
* 'compareseq' usually chooses the one at the beginning,
|
||||||
* but usually it is cleaner to consider the following identical line
|
* but usually it is cleaner to consider the following identical line
|
||||||
* to be the "change".
|
* to be the "change".
|
||||||
*
|
*
|
||||||
* This is extracted verbatim from analyze.c (GNU diffutils-2.7).
|
* This is extracted verbatim from analyze.c (GNU diffutils-2.7).
|
||||||
*/
|
*/
|
||||||
public function _shift_boundaries ($lines, &$changed, $other_changed) {
|
public function _shift_boundaries ($lines, &$changed, $other_changed) {
|
||||||
$i = 0;
|
$i = 0;
|
||||||
$j = 0;
|
$j = 0;
|
||||||
|
|
||||||
@ -423,37 +423,37 @@ class _DiffEngine
|
|||||||
$other_len = sizeof($other_changed);
|
$other_len = sizeof($other_changed);
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
/*
|
/*
|
||||||
* Scan forwards to find beginning of another run of changes.
|
* Scan forwards to find beginning of another run of changes.
|
||||||
* Also keep track of the corresponding point in the other file.
|
* Also keep track of the corresponding point in the other file.
|
||||||
*
|
*
|
||||||
* Throughout this code, $i and $j are adjusted together so that
|
* Throughout this code, $i and $j are adjusted together so that
|
||||||
* the first $i elements of $changed and the first $j elements
|
* the first $i elements of $changed and the first $j elements
|
||||||
* of $other_changed both contain the same number of zeros
|
* of $other_changed both contain the same number of zeros
|
||||||
* (unchanged lines).
|
* (unchanged lines).
|
||||||
* Furthermore, $j is always kept so that $j == $other_len or
|
* Furthermore, $j is always kept so that $j == $other_len or
|
||||||
* $other_changed[$j] == false.
|
* $other_changed[$j] == false.
|
||||||
*/
|
*/
|
||||||
while ($j < $other_len && $other_changed[$j])
|
while ($j < $other_len && $other_changed[$j])
|
||||||
$j++;
|
$j++;
|
||||||
|
|
||||||
while ($i < $len && ! $changed[$i]) {
|
while ($i < $len && ! $changed[$i]) {
|
||||||
USE_ASSERTS && assert('$j < $other_len && ! $other_changed[$j]');
|
USE_ASSERTS && assert('$j < $other_len && ! $other_changed[$j]');
|
||||||
$i++; $j++;
|
$i++; $j++;
|
||||||
while ($j < $other_len && $other_changed[$j])
|
while ($j < $other_len && $other_changed[$j])
|
||||||
$j++;
|
$j++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($i == $len)
|
if ($i == $len)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
$start = $i;
|
$start = $i;
|
||||||
|
|
||||||
// Find the end of this run of changes.
|
// Find the end of this run of changes.
|
||||||
while (++$i < $len && $changed[$i])
|
while (++$i < $len && $changed[$i])
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
/*
|
/*
|
||||||
* Record the length of this run of changes, so that
|
* Record the length of this run of changes, so that
|
||||||
* we can later determine whether the run has grown.
|
* we can later determine whether the run has grown.
|
||||||
@ -466,15 +466,15 @@ class _DiffEngine
|
|||||||
* This merges with previous changed regions.
|
* This merges with previous changed regions.
|
||||||
*/
|
*/
|
||||||
while ($start > 0 && $lines[$start - 1] == $lines[$i - 1]) {
|
while ($start > 0 && $lines[$start - 1] == $lines[$i - 1]) {
|
||||||
$changed[--$start] = 1;
|
$changed[--$start] = 1;
|
||||||
$changed[--$i] = false;
|
$changed[--$i] = false;
|
||||||
while ($start > 0 && $changed[$start - 1])
|
while ($start > 0 && $changed[$start - 1])
|
||||||
$start--;
|
$start--;
|
||||||
USE_ASSERTS && assert('$j > 0');
|
USE_ASSERTS && assert('$j > 0');
|
||||||
while ($other_changed[--$j])
|
while ($other_changed[--$j])
|
||||||
continue;
|
continue;
|
||||||
USE_ASSERTS && assert('$j >= 0 && !$other_changed[$j]');
|
USE_ASSERTS && assert('$j >= 0 && !$other_changed[$j]');
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set CORRESPONDING to the end of the changed run, at the last
|
* Set CORRESPONDING to the end of the changed run, at the last
|
||||||
@ -491,35 +491,35 @@ class _DiffEngine
|
|||||||
* the changed region is moved forward as far as possible.
|
* the changed region is moved forward as far as possible.
|
||||||
*/
|
*/
|
||||||
while ($i < $len && $lines[$start] == $lines[$i]) {
|
while ($i < $len && $lines[$start] == $lines[$i]) {
|
||||||
$changed[$start++] = false;
|
$changed[$start++] = false;
|
||||||
$changed[$i++] = 1;
|
$changed[$i++] = 1;
|
||||||
while ($i < $len && $changed[$i])
|
while ($i < $len && $changed[$i])
|
||||||
$i++;
|
$i++;
|
||||||
|
|
||||||
USE_ASSERTS && assert('$j < $other_len && ! $other_changed[$j]');
|
USE_ASSERTS && assert('$j < $other_len && ! $other_changed[$j]');
|
||||||
$j++;
|
$j++;
|
||||||
if ($j < $other_len && $other_changed[$j]) {
|
if ($j < $other_len && $other_changed[$j]) {
|
||||||
$corresponding = $i;
|
$corresponding = $i;
|
||||||
while ($j < $other_len && $other_changed[$j])
|
while ($j < $other_len && $other_changed[$j])
|
||||||
$j++;
|
$j++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} while ($runlength != $i - $start);
|
} while ($runlength != $i - $start);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If possible, move the fully-merged run of changes
|
* If possible, move the fully-merged run of changes
|
||||||
* back to a corresponding run in the other file.
|
* back to a corresponding run in the other file.
|
||||||
*/
|
*/
|
||||||
while ($corresponding < $i) {
|
while ($corresponding < $i) {
|
||||||
$changed[--$start] = 1;
|
$changed[--$start] = 1;
|
||||||
$changed[--$i] = 0;
|
$changed[--$i] = 0;
|
||||||
USE_ASSERTS && assert('$j > 0');
|
USE_ASSERTS && assert('$j > 0');
|
||||||
while ($other_changed[--$j])
|
while ($other_changed[--$j])
|
||||||
continue;
|
continue;
|
||||||
USE_ASSERTS && assert('$j >= 0 && !$other_changed[$j]');
|
USE_ASSERTS && assert('$j >= 0 && !$other_changed[$j]');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -531,138 +531,138 @@ class Diff
|
|||||||
{
|
{
|
||||||
public static $html_cleaner_class = null;
|
public static $html_cleaner_class = null;
|
||||||
|
|
||||||
var $edits;
|
var $edits;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
* Computes diff between sequences of strings.
|
* Computes diff between sequences of strings.
|
||||||
*
|
*
|
||||||
* @param $from_lines array An array of strings.
|
* @param $from_lines array An array of strings.
|
||||||
* (Typically these are lines from a file.)
|
* (Typically these are lines from a file.)
|
||||||
* @param $to_lines array An array of strings.
|
* @param $to_lines array An array of strings.
|
||||||
*/
|
*/
|
||||||
public function Diff($from_lines, $to_lines) {
|
public function Diff($from_lines, $to_lines) {
|
||||||
$eng = new _DiffEngine;
|
$eng = new _DiffEngine;
|
||||||
$this->edits = $eng->diff($from_lines, $to_lines);
|
$this->edits = $eng->diff($from_lines, $to_lines);
|
||||||
//$this->_check($from_lines, $to_lines);
|
//$this->_check($from_lines, $to_lines);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compute reversed Diff.
|
* Compute reversed Diff.
|
||||||
*
|
*
|
||||||
* SYNOPSIS:
|
* SYNOPSIS:
|
||||||
*
|
*
|
||||||
* $diff = new Diff($lines1, $lines2);
|
* $diff = new Diff($lines1, $lines2);
|
||||||
* $rev = $diff->reverse();
|
* $rev = $diff->reverse();
|
||||||
* @return object A Diff object representing the inverse of the
|
* @return object A Diff object representing the inverse of the
|
||||||
* original diff.
|
* original diff.
|
||||||
*/
|
*/
|
||||||
public function reverse () {
|
public function reverse () {
|
||||||
$rev = $this;
|
$rev = $this;
|
||||||
$rev->edits = array();
|
$rev->edits = array();
|
||||||
foreach ($this->edits as $edit) {
|
foreach ($this->edits as $edit) {
|
||||||
$rev->edits[] = $edit->reverse();
|
$rev->edits[] = $edit->reverse();
|
||||||
}
|
}
|
||||||
return $rev;
|
return $rev;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check for empty diff.
|
* Check for empty diff.
|
||||||
*
|
*
|
||||||
* @return bool True iff two sequences were identical.
|
* @return bool True iff two sequences were identical.
|
||||||
*/
|
*/
|
||||||
public function isEmpty () {
|
public function isEmpty () {
|
||||||
foreach ($this->edits as $edit) {
|
foreach ($this->edits as $edit) {
|
||||||
if ($edit->type != 'copy')
|
if ($edit->type != 'copy')
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compute the length of the Longest Common Subsequence (LCS).
|
* Compute the length of the Longest Common Subsequence (LCS).
|
||||||
*
|
*
|
||||||
* This is mostly for diagnostic purposed.
|
* This is mostly for diagnostic purposed.
|
||||||
*
|
*
|
||||||
* @return int The length of the LCS.
|
* @return int The length of the LCS.
|
||||||
*/
|
*/
|
||||||
public function lcs () {
|
public function lcs () {
|
||||||
$lcs = 0;
|
$lcs = 0;
|
||||||
foreach ($this->edits as $edit) {
|
foreach ($this->edits as $edit) {
|
||||||
if ($edit->type == 'copy')
|
if ($edit->type == 'copy')
|
||||||
$lcs += sizeof($edit->orig);
|
$lcs += sizeof($edit->orig);
|
||||||
}
|
}
|
||||||
return $lcs;
|
return $lcs;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the original set of lines.
|
* Get the original set of lines.
|
||||||
*
|
*
|
||||||
* This reconstructs the $from_lines parameter passed to the
|
* This reconstructs the $from_lines parameter passed to the
|
||||||
* constructor.
|
* constructor.
|
||||||
*
|
*
|
||||||
* @return array The original sequence of strings.
|
* @return array The original sequence of strings.
|
||||||
*/
|
*/
|
||||||
public function orig() {
|
public function orig() {
|
||||||
$lines = array();
|
$lines = array();
|
||||||
|
|
||||||
foreach ($this->edits as $edit) {
|
foreach ($this->edits as $edit) {
|
||||||
if ($edit->orig)
|
if ($edit->orig)
|
||||||
array_splice($lines, sizeof($lines), 0, $edit->orig);
|
array_splice($lines, sizeof($lines), 0, $edit->orig);
|
||||||
}
|
}
|
||||||
return $lines;
|
return $lines;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the final set of lines.
|
* Get the final set of lines.
|
||||||
*
|
*
|
||||||
* This reconstructs the $to_lines parameter passed to the
|
* This reconstructs the $to_lines parameter passed to the
|
||||||
* constructor.
|
* constructor.
|
||||||
*
|
*
|
||||||
* @return array The sequence of strings.
|
* @return array The sequence of strings.
|
||||||
*/
|
*/
|
||||||
public function finaltext() {
|
public function finaltext() {
|
||||||
$lines = array();
|
$lines = array();
|
||||||
|
|
||||||
foreach ($this->edits as $edit) {
|
foreach ($this->edits as $edit) {
|
||||||
if ($edit->final)
|
if ($edit->final)
|
||||||
array_splice($lines, sizeof($lines), 0, $edit->final);
|
array_splice($lines, sizeof($lines), 0, $edit->final);
|
||||||
}
|
}
|
||||||
return $lines;
|
return $lines;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check a Diff for validity.
|
* Check a Diff for validity.
|
||||||
*
|
*
|
||||||
* This is here only for debugging purposes.
|
* This is here only for debugging purposes.
|
||||||
*/
|
*/
|
||||||
public function _check ($from_lines, $to_lines) {
|
public function _check ($from_lines, $to_lines) {
|
||||||
if (serialize($from_lines) != serialize($this->orig()))
|
if (serialize($from_lines) != serialize($this->orig()))
|
||||||
trigger_error("Reconstructed original doesn't match", E_USER_ERROR);
|
trigger_error("Reconstructed original doesn't match", E_USER_ERROR);
|
||||||
if (serialize($to_lines) != serialize($this->finaltext()))
|
if (serialize($to_lines) != serialize($this->finaltext()))
|
||||||
trigger_error("Reconstructed final doesn't match", E_USER_ERROR);
|
trigger_error("Reconstructed final doesn't match", E_USER_ERROR);
|
||||||
|
|
||||||
$rev = $this->reverse();
|
$rev = $this->reverse();
|
||||||
if (serialize($to_lines) != serialize($rev->orig()))
|
if (serialize($to_lines) != serialize($rev->orig()))
|
||||||
trigger_error("Reversed original doesn't match", E_USER_ERROR);
|
trigger_error("Reversed original doesn't match", E_USER_ERROR);
|
||||||
if (serialize($from_lines) != serialize($rev->finaltext()))
|
if (serialize($from_lines) != serialize($rev->finaltext()))
|
||||||
trigger_error("Reversed final doesn't match", E_USER_ERROR);
|
trigger_error("Reversed final doesn't match", E_USER_ERROR);
|
||||||
|
|
||||||
|
|
||||||
$prevtype = 'none';
|
$prevtype = 'none';
|
||||||
foreach ($this->edits as $edit) {
|
foreach ($this->edits as $edit) {
|
||||||
if ( $prevtype == $edit->type )
|
if ( $prevtype == $edit->type )
|
||||||
trigger_error("Edit sequence is non-optimal", E_USER_ERROR);
|
trigger_error("Edit sequence is non-optimal", E_USER_ERROR);
|
||||||
$prevtype = $edit->type;
|
$prevtype = $edit->type;
|
||||||
}
|
}
|
||||||
|
|
||||||
$lcs = $this->lcs();
|
$lcs = $this->lcs();
|
||||||
trigger_error("Diff okay: LCS = $lcs", E_USER_NOTICE);
|
trigger_error("Diff okay: LCS = $lcs", E_USER_NOTICE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempt to clean invalid HTML, which messes up diffs.
|
* Attempt to clean invalid HTML, which messes up diffs.
|
||||||
* This cleans code if possible, using an instance of HTMLCleaner
|
* This cleans code if possible, using an instance of HTMLCleaner
|
||||||
*
|
*
|
||||||
@ -750,7 +750,7 @@ class Diff
|
|||||||
else $rechunked[$listName][] = $item;
|
else $rechunked[$listName][] = $item;
|
||||||
|
|
||||||
if($lookForTag && !$tagStack[$listName] && isset($item[0]) && $item[0] == "<"
|
if($lookForTag && !$tagStack[$listName] && isset($item[0]) && $item[0] == "<"
|
||||||
&& substr($item,0,2) != "</") {
|
&& substr($item,0,2) != "</") {
|
||||||
$tagStack[$listName] = 1;
|
$tagStack[$listName] = 1;
|
||||||
} else if($tagStack[$listName]) {
|
} else if($tagStack[$listName]) {
|
||||||
if(substr($item,0,2) == "</") $tagStack[$listName]--;
|
if(substr($item,0,2) == "</") $tagStack[$listName]--;
|
||||||
@ -830,54 +830,54 @@ class Diff
|
|||||||
class MappedDiff
|
class MappedDiff
|
||||||
extends Diff
|
extends Diff
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*
|
*
|
||||||
* Computes diff between sequences of strings.
|
* Computes diff between sequences of strings.
|
||||||
*
|
*
|
||||||
* This can be used to compute things like
|
* This can be used to compute things like
|
||||||
* case-insensitve diffs, or diffs which ignore
|
* case-insensitve diffs, or diffs which ignore
|
||||||
* changes in white-space.
|
* changes in white-space.
|
||||||
*
|
*
|
||||||
* @param $from_lines array An array of strings.
|
* @param $from_lines array An array of strings.
|
||||||
* (Typically these are lines from a file.)
|
* (Typically these are lines from a file.)
|
||||||
*
|
*
|
||||||
* @param $to_lines array An array of strings.
|
* @param $to_lines array An array of strings.
|
||||||
*
|
*
|
||||||
* @param $mapped_from_lines array This array should
|
* @param $mapped_from_lines array This array should
|
||||||
* have the same size number of elements as $from_lines.
|
* have the same size number of elements as $from_lines.
|
||||||
* The elements in $mapped_from_lines and
|
* The elements in $mapped_from_lines and
|
||||||
* $mapped_to_lines are what is actually compared
|
* $mapped_to_lines are what is actually compared
|
||||||
* when computing the diff.
|
* when computing the diff.
|
||||||
*
|
*
|
||||||
* @param $mapped_to_lines array This array should
|
* @param $mapped_to_lines array This array should
|
||||||
* have the same number of elements as $to_lines.
|
* have the same number of elements as $to_lines.
|
||||||
*/
|
*/
|
||||||
public function MappedDiff($from_lines, $to_lines,
|
public function MappedDiff($from_lines, $to_lines,
|
||||||
$mapped_from_lines, $mapped_to_lines) {
|
$mapped_from_lines, $mapped_to_lines) {
|
||||||
|
|
||||||
assert(sizeof($from_lines) == sizeof($mapped_from_lines));
|
assert(sizeof($from_lines) == sizeof($mapped_from_lines));
|
||||||
assert(sizeof($to_lines) == sizeof($mapped_to_lines));
|
assert(sizeof($to_lines) == sizeof($mapped_to_lines));
|
||||||
|
|
||||||
$this->Diff($mapped_from_lines, $mapped_to_lines);
|
$this->Diff($mapped_from_lines, $mapped_to_lines);
|
||||||
|
|
||||||
$xi = $yi = 0;
|
$xi = $yi = 0;
|
||||||
// Optimizing loop invariants:
|
// Optimizing loop invariants:
|
||||||
// http://phplens.com/lens/php-book/optimizing-debugging-php.php
|
// http://phplens.com/lens/php-book/optimizing-debugging-php.php
|
||||||
for ($i = 0, $max = sizeof($this->edits); $i < $max; $i++) {
|
for ($i = 0, $max = sizeof($this->edits); $i < $max; $i++) {
|
||||||
$orig = &$this->edits[$i]->orig;
|
$orig = &$this->edits[$i]->orig;
|
||||||
if (is_array($orig)) {
|
if (is_array($orig)) {
|
||||||
$orig = array_slice($from_lines, $xi, sizeof($orig));
|
$orig = array_slice($from_lines, $xi, sizeof($orig));
|
||||||
$xi += sizeof($orig);
|
$xi += sizeof($orig);
|
||||||
}
|
}
|
||||||
|
|
||||||
$final = &$this->edits[$i]->final;
|
$final = &$this->edits[$i]->final;
|
||||||
if (is_array($final)) {
|
if (is_array($final)) {
|
||||||
$final = array_slice($to_lines, $yi, sizeof($final));
|
$final = array_slice($to_lines, $yi, sizeof($final));
|
||||||
$yi += sizeof($final);
|
$yi += sizeof($final);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -140,7 +140,7 @@ abstract class BulkLoader extends ViewableData {
|
|||||||
|
|
||||||
//get all instances of the to be imported data object
|
//get all instances of the to be imported data object
|
||||||
if($this->deleteExistingRecords) {
|
if($this->deleteExistingRecords) {
|
||||||
DataObject::get($this->objectClass)->removeAll();
|
DataObject::get($this->objectClass)->removeAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->processAll($filepath);
|
return $this->processAll($filepath);
|
||||||
@ -273,12 +273,12 @@ class BulkLoader_Result extends Object {
|
|||||||
* </code>
|
* </code>
|
||||||
*/
|
*/
|
||||||
protected $created = array();
|
protected $created = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var array (see {@link $created})
|
* @var array (see {@link $created})
|
||||||
*/
|
*/
|
||||||
protected $updated = array();
|
protected $updated = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var array (see {@link $created})
|
* @var array (see {@link $created})
|
||||||
*/
|
*/
|
||||||
@ -290,7 +290,7 @@ class BulkLoader_Result extends Object {
|
|||||||
* one of 3 strings: "created", "updated", or "deleted"
|
* one of 3 strings: "created", "updated", or "deleted"
|
||||||
*/
|
*/
|
||||||
protected $lastChange = array();
|
protected $lastChange = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the count of all objects which were
|
* Returns the count of all objects which were
|
||||||
* created or updated.
|
* created or updated.
|
||||||
@ -408,5 +408,4 @@ class BulkLoader_Result extends Object {
|
|||||||
|
|
||||||
return $set;
|
return $set;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -18,15 +18,15 @@ class DevelopmentAdmin extends Controller {
|
|||||||
);
|
);
|
||||||
|
|
||||||
static $allowed_actions = array(
|
static $allowed_actions = array(
|
||||||
'index',
|
'index',
|
||||||
'tests',
|
'tests',
|
||||||
'jstests',
|
'jstests',
|
||||||
'tasks',
|
'tasks',
|
||||||
'viewmodel',
|
'viewmodel',
|
||||||
'build',
|
'build',
|
||||||
'reset',
|
'reset',
|
||||||
'viewcode'
|
'viewcode'
|
||||||
);
|
);
|
||||||
|
|
||||||
public function init() {
|
public function init() {
|
||||||
parent::init();
|
parent::init();
|
||||||
@ -56,7 +56,7 @@ class DevelopmentAdmin extends Controller {
|
|||||||
$matched = false;
|
$matched = false;
|
||||||
if(isset($_FILE_TO_URL_MAPPING[$testPath])) {
|
if(isset($_FILE_TO_URL_MAPPING[$testPath])) {
|
||||||
$matched = true;
|
$matched = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
$testPath = dirname($testPath);
|
$testPath = dirname($testPath);
|
||||||
}
|
}
|
||||||
|
@ -8,14 +8,14 @@
|
|||||||
*
|
*
|
||||||
* <code>
|
* <code>
|
||||||
* public function testMyForm() {
|
* public function testMyForm() {
|
||||||
* // Visit a URL
|
* // Visit a URL
|
||||||
* $this->get("your/url");
|
* $this->get("your/url");
|
||||||
*
|
*
|
||||||
* // Submit a form on the page that you get in response
|
* // Submit a form on the page that you get in response
|
||||||
* $this->submitForm("MyForm_ID", array("Email" => "invalid email ^&*&^"));
|
* $this->submitForm("MyForm_ID", array("Email" => "invalid email ^&*&^"));
|
||||||
*
|
*
|
||||||
* // Validate the content that is returned
|
* // Validate the content that is returned
|
||||||
* $this->assertExactMatchBySelector("#MyForm_ID p.error", array("That email address is invalid."));
|
* $this->assertExactMatchBySelector("#MyForm_ID p.error", array("That email address is invalid."));
|
||||||
* }
|
* }
|
||||||
* </code>
|
* </code>
|
||||||
*
|
*
|
||||||
@ -70,10 +70,10 @@ class FunctionalTest extends SapphireTest {
|
|||||||
if($this->stat('use_draft_site')) {
|
if($this->stat('use_draft_site')) {
|
||||||
$this->useDraftSite();
|
$this->useDraftSite();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unprotect the site, tests are running with the assumption it's off. They will enable it on a case-by-case
|
// Unprotect the site, tests are running with the assumption it's off. They will enable it on a case-by-case
|
||||||
// basis.
|
// basis.
|
||||||
BasicAuth::protect_entire_site(false);
|
BasicAuth::protect_entire_site(false);
|
||||||
|
|
||||||
SecurityToken::disable();
|
SecurityToken::disable();
|
||||||
}
|
}
|
||||||
@ -193,8 +193,8 @@ class FunctionalTest extends SapphireTest {
|
|||||||
foreach($expectedMatches as $match) {
|
foreach($expectedMatches as $match) {
|
||||||
$this->assertTrue(
|
$this->assertTrue(
|
||||||
isset($actuals[$match]),
|
isset($actuals[$match]),
|
||||||
"Failed asserting the CSS selector '$selector' has a partial match to the expected elements:\n'"
|
"Failed asserting the CSS selector '$selector' has a partial match to the expected elements:\n'"
|
||||||
. implode("'\n'", $expectedMatches) . "'\n\n"
|
. implode("'\n'", $expectedMatches) . "'\n\n"
|
||||||
. "Instead the following elements were found:\n'" . implode("'\n'", array_keys($actuals)) . "'"
|
. "Instead the following elements were found:\n'" . implode("'\n'", array_keys($actuals)) . "'"
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
|
394
dev/Profiler.php
394
dev/Profiler.php
@ -16,224 +16,224 @@
|
|||||||
* @subpackage misc
|
* @subpackage misc
|
||||||
*/
|
*/
|
||||||
class Profiler {
|
class Profiler {
|
||||||
var $description;
|
var $description;
|
||||||
var $startTime;
|
var $startTime;
|
||||||
var $endTime;
|
var $endTime;
|
||||||
var $initTime;
|
var $initTime;
|
||||||
var $cur_timer;
|
var $cur_timer;
|
||||||
var $stack;
|
var $stack;
|
||||||
var $trail;
|
var $trail;
|
||||||
var $trace;
|
var $trace;
|
||||||
var $count;
|
var $count;
|
||||||
var $running;
|
var $running;
|
||||||
|
|
||||||
protected static $inst;
|
protected static $inst;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialise the timer. with the current micro time
|
* Initialise the timer. with the current micro time
|
||||||
*/
|
*/
|
||||||
public function Profiler( $output_enabled=false, $trace_enabled=false)
|
public function Profiler( $output_enabled=false, $trace_enabled=false)
|
||||||
{
|
{
|
||||||
$this->description = array();
|
$this->description = array();
|
||||||
$this->startTime = array();
|
$this->startTime = array();
|
||||||
$this->endTime = array();
|
$this->endTime = array();
|
||||||
$this->initTime = 0;
|
$this->initTime = 0;
|
||||||
$this->cur_timer = "";
|
$this->cur_timer = "";
|
||||||
$this->stack = array();
|
$this->stack = array();
|
||||||
$this->trail = "";
|
$this->trail = "";
|
||||||
$this->trace = "";
|
$this->trace = "";
|
||||||
$this->count = array();
|
$this->count = array();
|
||||||
$this->running = array();
|
$this->running = array();
|
||||||
$this->initTime = $this->getMicroTime();
|
$this->initTime = $this->getMicroTime();
|
||||||
$this->output_enabled = $output_enabled;
|
$this->output_enabled = $output_enabled;
|
||||||
$this->trace_enabled = $trace_enabled;
|
$this->trace_enabled = $trace_enabled;
|
||||||
$this->startTimer('unprofiled');
|
$this->startTimer('unprofiled');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Public Methods
|
// Public Methods
|
||||||
|
|
||||||
public static function init() {
|
public static function init() {
|
||||||
Deprecation::notice('3.1', 'The Profiler class is deprecated, use third party tools like XHProf instead');
|
Deprecation::notice('3.1', 'The Profiler class is deprecated, use third party tools like XHProf instead');
|
||||||
if(!self::$inst) self::$inst = new Profiler(true,true);
|
if(!self::$inst) self::$inst = new Profiler(true,true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function mark($name, $level2 = "", $desc = "") {
|
public static function mark($name, $level2 = "", $desc = "") {
|
||||||
if($level2 && $_GET['debug_profile'] > 1) $name .= " $level2";
|
if($level2 && $_GET['debug_profile'] > 1) $name .= " $level2";
|
||||||
|
|
||||||
if(!self::$inst) self::$inst = new Profiler(true,true);
|
if(!self::$inst) self::$inst = new Profiler(true,true);
|
||||||
|
|
||||||
self::$inst->startTimer($name, $desc);
|
self::$inst->startTimer($name, $desc);
|
||||||
}
|
}
|
||||||
public static function unmark($name, $level2 = "", $desc = "") {
|
public static function unmark($name, $level2 = "", $desc = "") {
|
||||||
if($level2 && $_GET['debug_profile'] > 1) $name .= " $level2";
|
if($level2 && $_GET['debug_profile'] > 1) $name .= " $level2";
|
||||||
|
|
||||||
if(!self::$inst) self::$inst = new Profiler(true,true);
|
if(!self::$inst) self::$inst = new Profiler(true,true);
|
||||||
|
|
||||||
self::$inst->stopTimer($name, $desc);
|
self::$inst->stopTimer($name, $desc);
|
||||||
}
|
}
|
||||||
public static function show($showTrace = false) {
|
public static function show($showTrace = false) {
|
||||||
if(!self::$inst) self::$inst = new Profiler(true,true);
|
if(!self::$inst) self::$inst = new Profiler(true,true);
|
||||||
|
|
||||||
echo "<div style=\"position: absolute; z-index: 100000; top: 20px; left: 20px; background-color: white;"
|
echo "<div style=\"position: absolute; z-index: 100000; top: 20px; left: 20px; background-color: white;"
|
||||||
. " padding: 20px; border: 1px #AAA solid; height: 80%; overflow: auto;\">";
|
. " padding: 20px; border: 1px #AAA solid; height: 80%; overflow: auto;\">";
|
||||||
echo "<p><a href=\"#\" onclick=\"this.parentNode.parentNode.style.display = 'none'; return false;\">"
|
echo "<p><a href=\"#\" onclick=\"this.parentNode.parentNode.style.display = 'none'; return false;\">"
|
||||||
. "(Click to close)</a></p>";
|
. "(Click to close)</a></p>";
|
||||||
self::$inst->printTimers();
|
self::$inst->printTimers();
|
||||||
if($showTrace) self::$inst->printTrace();
|
if($showTrace) self::$inst->printTrace();
|
||||||
echo "</div>";
|
echo "</div>";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start an individual timer
|
* Start an individual timer
|
||||||
* This will pause the running timer and place it on a stack.
|
* This will pause the running timer and place it on a stack.
|
||||||
* @param string $name name of the timer
|
* @param string $name name of the timer
|
||||||
* @param string optional $desc description of the timer
|
* @param string optional $desc description of the timer
|
||||||
*/
|
*/
|
||||||
public function startTimer($name, $desc="" ){
|
public function startTimer($name, $desc="" ){
|
||||||
$this->trace.="start $name\n";
|
$this->trace.="start $name\n";
|
||||||
$n=array_push( $this->stack, $this->cur_timer );
|
$n=array_push( $this->stack, $this->cur_timer );
|
||||||
$this->__suspendTimer( $this->stack[$n-1] );
|
$this->__suspendTimer( $this->stack[$n-1] );
|
||||||
$this->startTime[$name] = $this->getMicroTime();
|
$this->startTime[$name] = $this->getMicroTime();
|
||||||
$this->cur_timer=$name;
|
$this->cur_timer=$name;
|
||||||
$this->description[$name] = $desc;
|
$this->description[$name] = $desc;
|
||||||
if (!array_key_exists($name,$this->count))
|
if (!array_key_exists($name,$this->count))
|
||||||
$this->count[$name] = 1;
|
$this->count[$name] = 1;
|
||||||
else
|
else
|
||||||
$this->count[$name]++;
|
$this->count[$name]++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stop an individual timer
|
* Stop an individual timer
|
||||||
* Restart the timer that was running before this one
|
* Restart the timer that was running before this one
|
||||||
* @param string $name name of the timer
|
* @param string $name name of the timer
|
||||||
*/
|
*/
|
||||||
public function stopTimer($name){
|
public function stopTimer($name){
|
||||||
$this->trace.="stop $name\n";
|
$this->trace.="stop $name\n";
|
||||||
$this->endTime[$name] = $this->getMicroTime();
|
$this->endTime[$name] = $this->getMicroTime();
|
||||||
if (!array_key_exists($name, $this->running))
|
if (!array_key_exists($name, $this->running))
|
||||||
$this->running[$name] = $this->elapsedTime($name);
|
$this->running[$name] = $this->elapsedTime($name);
|
||||||
else
|
else
|
||||||
$this->running[$name] += $this->elapsedTime($name);
|
$this->running[$name] += $this->elapsedTime($name);
|
||||||
$this->cur_timer=array_pop($this->stack);
|
$this->cur_timer=array_pop($this->stack);
|
||||||
$this->__resumeTimer($this->cur_timer);
|
$this->__resumeTimer($this->cur_timer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* measure the elapsed time of a timer without stoping the timer if
|
* measure the elapsed time of a timer without stoping the timer if
|
||||||
* it is still running
|
* it is still running
|
||||||
*/
|
*/
|
||||||
public function elapsedTime($name){
|
public function elapsedTime($name){
|
||||||
// This shouldn't happen, but it does once.
|
// This shouldn't happen, but it does once.
|
||||||
if (!array_key_exists($name,$this->startTime))
|
if (!array_key_exists($name,$this->startTime))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if(array_key_exists($name,$this->endTime)){
|
if(array_key_exists($name,$this->endTime)){
|
||||||
return ($this->endTime[$name] - $this->startTime[$name]);
|
return ($this->endTime[$name] - $this->startTime[$name]);
|
||||||
} else {
|
} else {
|
||||||
$now=$this->getMicroTime();
|
$now=$this->getMicroTime();
|
||||||
return ($now - $this->startTime[$name]);
|
return ($now - $this->startTime[$name]);
|
||||||
}
|
}
|
||||||
}//end start_time
|
}//end start_time
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Measure the elapsed time since the profile class was initialised
|
* Measure the elapsed time since the profile class was initialised
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public function elapsedOverall(){
|
public function elapsedOverall(){
|
||||||
$oaTime = $this->getMicroTime() - $this->initTime;
|
$oaTime = $this->getMicroTime() - $this->initTime;
|
||||||
return($oaTime);
|
return($oaTime);
|
||||||
}//end start_time
|
}//end start_time
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* print out a log of all the timers that were registered
|
* print out a log of all the timers that were registered
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public function printTimers($enabled=false)
|
public function printTimers($enabled=false)
|
||||||
{
|
{
|
||||||
if($this->output_enabled||$enabled){
|
if($this->output_enabled||$enabled){
|
||||||
$TimedTotal = 0;
|
$TimedTotal = 0;
|
||||||
$tot_perc = 0;
|
$tot_perc = 0;
|
||||||
ksort($this->description);
|
ksort($this->description);
|
||||||
print("<pre>\n");
|
print("<pre>\n");
|
||||||
$oaTime = $this->getMicroTime() - $this->initTime;
|
$oaTime = $this->getMicroTime() - $this->initTime;
|
||||||
echo"============================================================================\n";
|
echo"============================================================================\n";
|
||||||
echo " PROFILER OUTPUT\n";
|
echo " PROFILER OUTPUT\n";
|
||||||
echo"============================================================================\n";
|
echo"============================================================================\n";
|
||||||
print( "Calls Time Routine\n");
|
print( "Calls Time Routine\n");
|
||||||
echo"-----------------------------------------------------------------------------\n";
|
echo"-----------------------------------------------------------------------------\n";
|
||||||
while (list ($key, $val) = each ($this->description)) {
|
while (list ($key, $val) = each ($this->description)) {
|
||||||
$t = $this->elapsedTime($key);
|
$t = $this->elapsedTime($key);
|
||||||
$total = $this->running[$key];
|
$total = $this->running[$key];
|
||||||
$count = $this->count[$key];
|
$count = $this->count[$key];
|
||||||
$TimedTotal += $total;
|
$TimedTotal += $total;
|
||||||
$perc = ($total/$oaTime)*100;
|
$perc = ($total/$oaTime)*100;
|
||||||
$tot_perc+=$perc;
|
$tot_perc+=$perc;
|
||||||
// $perc=sprintf("%3.2f", $perc );
|
// $perc=sprintf("%3.2f", $perc );
|
||||||
$lines[ sprintf( "%3d %3.4f ms (%3.2f %%) %s\n", $count, $total*1000, $perc, $key) ] = $total;
|
$lines[ sprintf( "%3d %3.4f ms (%3.2f %%) %s\n", $count, $total*1000, $perc, $key) ] = $total;
|
||||||
}
|
}
|
||||||
arsort($lines);
|
arsort($lines);
|
||||||
foreach($lines as $line => $total) {
|
foreach($lines as $line => $total) {
|
||||||
echo $line;
|
echo $line;
|
||||||
}
|
}
|
||||||
|
|
||||||
echo "\n";
|
echo "\n";
|
||||||
|
|
||||||
$missed=$oaTime-$TimedTotal;
|
$missed=$oaTime-$TimedTotal;
|
||||||
$perc = ($missed/$oaTime)*100;
|
$perc = ($missed/$oaTime)*100;
|
||||||
$tot_perc+=$perc;
|
$tot_perc+=$perc;
|
||||||
// $perc=sprintf("%3.2f", $perc );
|
// $perc=sprintf("%3.2f", $perc );
|
||||||
printf( " %3.4f ms (%3.2f %%) %s\n", $missed*1000,$perc, "Missed");
|
printf( " %3.4f ms (%3.2f %%) %s\n", $missed*1000,$perc, "Missed");
|
||||||
|
|
||||||
echo"============================================================================\n";
|
echo"============================================================================\n";
|
||||||
|
|
||||||
printf( " %3.4f ms (%3.2f %%) %s\n", $oaTime*1000,$tot_perc, "OVERALL TIME");
|
printf( " %3.4f ms (%3.2f %%) %s\n", $oaTime*1000,$tot_perc, "OVERALL TIME");
|
||||||
|
|
||||||
echo"============================================================================\n";
|
echo"============================================================================\n";
|
||||||
|
|
||||||
print("</pre>");
|
print("</pre>");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function printTrace( $enabled=false )
|
public function printTrace( $enabled=false )
|
||||||
{
|
{
|
||||||
if($this->trace_enabled||$enabled){
|
if($this->trace_enabled||$enabled){
|
||||||
print("<pre>");
|
print("<pre>");
|
||||||
print("Trace\n$this->trace\n\n");
|
print("Trace\n$this->trace\n\n");
|
||||||
print("</pre>");
|
print("</pre>");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Internal Use Only Functions
|
/// Internal Use Only Functions
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the current time as accuratly as possible
|
* Get the current time as accuratly as possible
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public function getMicroTime(){
|
public function getMicroTime(){
|
||||||
$tmp=explode(' ', microtime());
|
$tmp=explode(' ', microtime());
|
||||||
$rt=$tmp[0]+$tmp[1];
|
$rt=$tmp[0]+$tmp[1];
|
||||||
return $rt;
|
return $rt;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* resume an individual timer
|
* resume an individual timer
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public function __resumeTimer($name){
|
public function __resumeTimer($name){
|
||||||
$this->trace.="resume $name\n";
|
$this->trace.="resume $name\n";
|
||||||
$this->startTime[$name] = $this->getMicroTime();
|
$this->startTime[$name] = $this->getMicroTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* suspend an individual timer
|
* suspend an individual timer
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public function __suspendTimer($name){
|
public function __suspendTimer($name){
|
||||||
$this->trace.="suspend $name\n";
|
$this->trace.="suspend $name\n";
|
||||||
$this->endTime[$name] = $this->getMicroTime();
|
$this->endTime[$name] = $this->getMicroTime();
|
||||||
if (!array_key_exists($name, $this->running))
|
if (!array_key_exists($name, $this->running))
|
||||||
$this->running[$name] = $this->elapsedTime($name);
|
$this->running[$name] = $this->elapsedTime($name);
|
||||||
else
|
else
|
||||||
$this->running[$name] += $this->elapsedTime($name);
|
$this->running[$name] += $this->elapsedTime($name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -83,17 +83,17 @@ class SapphireTestReporter implements PHPUnit_Framework_TestListener {
|
|||||||
public function __construct() {
|
public function __construct() {
|
||||||
@include_once 'Benchmark/Timer.php';
|
@include_once 'Benchmark/Timer.php';
|
||||||
if(class_exists('Benchmark_Timer')) {
|
if(class_exists('Benchmark_Timer')) {
|
||||||
$this->timer = new Benchmark_Timer();
|
$this->timer = new Benchmark_Timer();
|
||||||
$this->hasTimer = true;
|
$this->hasTimer = true;
|
||||||
} else {
|
} else {
|
||||||
$this->hasTimer = false;
|
$this->hasTimer = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->suiteResults = array(
|
$this->suiteResults = array(
|
||||||
'suites' => array(), // array of suites run
|
'suites' => array(), // array of suites run
|
||||||
'hasTimer' => $this->hasTimer, // availability of PEAR Benchmark_Timer
|
'hasTimer' => $this->hasTimer, // availability of PEAR Benchmark_Timer
|
||||||
'totalTests' => 0 // total number of tests run
|
'totalTests' => 0 // total number of tests run
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -117,14 +117,14 @@ class SapphireTestReporter implements PHPUnit_Framework_TestListener {
|
|||||||
public function startTestSuite( PHPUnit_Framework_TestSuite $suite) {
|
public function startTestSuite( PHPUnit_Framework_TestSuite $suite) {
|
||||||
if(strlen($suite->getName())) {
|
if(strlen($suite->getName())) {
|
||||||
$this->endCurrentTestSuite();
|
$this->endCurrentTestSuite();
|
||||||
$this->currentSuite = array(
|
$this->currentSuite = array(
|
||||||
'suite' => $suite, // the test suite
|
'suite' => $suite, // the test suite
|
||||||
'tests' => array(), // the tests in the suite
|
'tests' => array(), // the tests in the suite
|
||||||
'errors' => 0, // number of tests with errors (including setup errors)
|
'errors' => 0, // number of tests with errors (including setup errors)
|
||||||
'failures' => 0, // number of tests which failed
|
'failures' => 0, // number of tests which failed
|
||||||
'incomplete' => 0, // number of tests that were not completed correctly
|
'incomplete' => 0, // number of tests that were not completed correctly
|
||||||
'error' => null); // Any error encountered during setup of the test suite
|
'error' => null); // Any error encountered during setup of the test suite
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -226,7 +226,7 @@ class SapphireTestReporter implements PHPUnit_Framework_TestListener {
|
|||||||
$this->currentSuite['incomplete']++;
|
$this->currentSuite['incomplete']++;
|
||||||
$this->addStatus(TEST_INCOMPLETE, $e->toString(), $this->getTestException($test, $e), $e->getTrace());
|
$this->addStatus(TEST_INCOMPLETE, $e->toString(), $this->getTestException($test, $e), $e->getTrace());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Not used
|
* Not used
|
||||||
*
|
*
|
||||||
@ -257,7 +257,7 @@ class SapphireTestReporter implements PHPUnit_Framework_TestListener {
|
|||||||
$this->currentTest = null;
|
$this->currentTest = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Upon completion of a test, records the execution time (if available) and adds the test to
|
* Upon completion of a test, records the execution time (if available) and adds the test to
|
||||||
* the tests performed in the current suite.
|
* the tests performed in the current suite.
|
||||||
*
|
*
|
||||||
|
@ -27,13 +27,13 @@ class SilverStripeListener implements PHPUnit_Framework_TestListener {
|
|||||||
|
|
||||||
public function startTest(PHPUnit_Framework_Test $test) {
|
public function startTest(PHPUnit_Framework_Test $test) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function endTest(PHPUnit_Framework_Test $test, $time) {
|
public function endTest(PHPUnit_Framework_Test $test, $time) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addError(PHPUnit_Framework_Test $test, Exception $e, $time) {
|
public function addError(PHPUnit_Framework_Test $test, Exception $e, $time) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time) {
|
public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ class TeamCityListener implements PHPUnit_Framework_TestListener {
|
|||||||
$class = get_class($test);
|
$class = get_class($test);
|
||||||
echo "##teamcity[testStarted name='{$class}.{$test->getName()}']\n";
|
echo "##teamcity[testStarted name='{$class}.{$test->getName()}']\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
public function endTest(PHPUnit_Framework_Test $test, $time) {
|
public function endTest(PHPUnit_Framework_Test $test, $time) {
|
||||||
$class = get_class($test);
|
$class = get_class($test);
|
||||||
echo "##teamcity[testFinished name='{$class}.{$test->getName()}' duration='$time']\n";
|
echo "##teamcity[testFinished name='{$class}.{$test->getName()}' duration='$time']\n";
|
||||||
@ -40,7 +40,7 @@ class TeamCityListener implements PHPUnit_Framework_TestListener {
|
|||||||
echo "##teamcity[testFailed type='exception' name='{$class}.{$test->getName()}' message='$message'"
|
echo "##teamcity[testFailed type='exception' name='{$class}.{$test->getName()}' message='$message'"
|
||||||
. " details='$trace']\n";
|
. " details='$trace']\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time) {
|
public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time) {
|
||||||
$class = get_class($test);
|
$class = get_class($test);
|
||||||
$message = $this->escape($e->getMessage());
|
$message = $this->escape($e->getMessage());
|
||||||
|
@ -13,15 +13,15 @@ class SS_TestListener implements PHPUnit_Framework_TestListener {
|
|||||||
|
|
||||||
public function addError(PHPUnit_Framework_Test $test, Exception $e, $time) {}
|
public function addError(PHPUnit_Framework_Test $test, Exception $e, $time) {}
|
||||||
|
|
||||||
public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time) {}
|
public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time) {}
|
||||||
|
|
||||||
public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time) {}
|
public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time) {}
|
||||||
|
|
||||||
public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time) {}
|
public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time) {}
|
||||||
|
|
||||||
public function startTest(PHPUnit_Framework_Test $test) {}
|
public function startTest(PHPUnit_Framework_Test $test) {}
|
||||||
|
|
||||||
public function endTest(PHPUnit_Framework_Test $test, $time) {}
|
public function endTest(PHPUnit_Framework_Test $test, $time) {}
|
||||||
|
|
||||||
public function startTestSuite(PHPUnit_Framework_TestSuite $suite) {
|
public function startTestSuite(PHPUnit_Framework_TestSuite $suite) {
|
||||||
$name = $suite->getName();
|
$name = $suite->getName();
|
||||||
@ -29,7 +29,7 @@ class SS_TestListener implements PHPUnit_Framework_TestListener {
|
|||||||
|
|
||||||
$this->class = new $name();
|
$this->class = new $name();
|
||||||
$this->class->setUpOnce();
|
$this->class->setUpOnce();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function endTestSuite(PHPUnit_Framework_TestSuite $suite) {
|
public function endTestSuite(PHPUnit_Framework_TestSuite $suite) {
|
||||||
$name = $suite->getName();
|
$name = $suite->getName();
|
||||||
|
@ -138,14 +138,14 @@ class PhpUnitWrapper implements IPhpUnitWrapper {
|
|||||||
public static function inst() {
|
public static function inst() {
|
||||||
|
|
||||||
if (self::$phpunit_wrapper == null) {
|
if (self::$phpunit_wrapper == null) {
|
||||||
if (fileExistsInIncludePath("/PHPUnit/Autoload.php")) {
|
if (fileExistsInIncludePath("/PHPUnit/Autoload.php")) {
|
||||||
self::$phpunit_wrapper = new PhpUnitWrapper_3_5();
|
self::$phpunit_wrapper = new PhpUnitWrapper_3_5();
|
||||||
} else
|
} else
|
||||||
if (fileExistsInIncludePath("/PHPUnit/Framework.php")) {
|
if (fileExistsInIncludePath("/PHPUnit/Framework.php")) {
|
||||||
self::$phpunit_wrapper = new PhpUnitWrapper_3_4();
|
self::$phpunit_wrapper = new PhpUnitWrapper_3_4();
|
||||||
} else {
|
} else {
|
||||||
self::$phpunit_wrapper = new PhpUnitWrapper();
|
self::$phpunit_wrapper = new PhpUnitWrapper();
|
||||||
}
|
}
|
||||||
self::$phpunit_wrapper->init();
|
self::$phpunit_wrapper->init();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -35,11 +35,11 @@ class PhpUnitWrapper_3_5 extends PhpUnitWrapper {
|
|||||||
protected function beforeRunTests() {
|
protected function beforeRunTests() {
|
||||||
|
|
||||||
if($this->getCoverageStatus()) {
|
if($this->getCoverageStatus()) {
|
||||||
$this->coverage = new PHP_CodeCoverage();
|
$this->coverage = new PHP_CodeCoverage();
|
||||||
$coverage = $this->coverage;
|
$coverage = $this->coverage;
|
||||||
|
|
||||||
$filter = $coverage->filter();
|
$filter = $coverage->filter();
|
||||||
$modules = $this->moduleDirectories();
|
$modules = $this->moduleDirectories();
|
||||||
|
|
||||||
foreach(TestRunner::$coverage_filter_dirs as $dir) {
|
foreach(TestRunner::$coverage_filter_dirs as $dir) {
|
||||||
if($dir[0] == '*') {
|
if($dir[0] == '*') {
|
||||||
|
@ -363,12 +363,12 @@ class Email extends ViewableData {
|
|||||||
* Validates the email address. Returns true of false
|
* Validates the email address. Returns true of false
|
||||||
*/
|
*/
|
||||||
public static function validEmailAddress($address) {
|
public static function validEmailAddress($address) {
|
||||||
if (function_exists('filter_var')) {
|
if (function_exists('filter_var')) {
|
||||||
return filter_var($address, FILTER_VALIDATE_EMAIL);
|
return filter_var($address, FILTER_VALIDATE_EMAIL);
|
||||||
} else {
|
} else {
|
||||||
return preg_match('#^([a-zA-Z0-9_+\.\-]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)'
|
return preg_match('#^([a-zA-Z0-9_+\.\-]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)'
|
||||||
. '|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$#', $address);
|
. '|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$#', $address);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -120,9 +120,9 @@ class Mailer {
|
|||||||
dieprintr($customheaders);
|
dieprintr($customheaders);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
$bodyIsUnicode = (strpos($htmlContent,"&#") !== false);
|
$bodyIsUnicode = (strpos($htmlContent,"&#") !== false);
|
||||||
$plainEncoding = "";
|
$plainEncoding = "";
|
||||||
|
|
||||||
// We generate plaintext content by default, but you can pass custom stuff
|
// We generate plaintext content by default, but you can pass custom stuff
|
||||||
$plainEncoding = '';
|
$plainEncoding = '';
|
||||||
@ -146,7 +146,7 @@ class Mailer {
|
|||||||
|
|
||||||
// Make the HTML part
|
// Make the HTML part
|
||||||
$headers["Content-Type"] = "text/html; charset=utf-8";
|
$headers["Content-Type"] = "text/html; charset=utf-8";
|
||||||
|
|
||||||
|
|
||||||
// Add basic wrapper tags if the body tag hasn't been given
|
// Add basic wrapper tags if the body tag hasn't been given
|
||||||
if(stripos($htmlContent, '<body') === false) {
|
if(stripos($htmlContent, '<body') === false) {
|
||||||
@ -199,14 +199,14 @@ class Mailer {
|
|||||||
$headers["From"] = $this->validEmailAddr($from);
|
$headers["From"] = $this->validEmailAddr($from);
|
||||||
|
|
||||||
// Messages with the X-SilverStripeMessageID header can be tracked
|
// Messages with the X-SilverStripeMessageID header can be tracked
|
||||||
if(isset($customheaders["X-SilverStripeMessageID"]) && defined('BOUNCE_EMAIL')) {
|
if(isset($customheaders["X-SilverStripeMessageID"]) && defined('BOUNCE_EMAIL')) {
|
||||||
$bounceAddress = BOUNCE_EMAIL;
|
$bounceAddress = BOUNCE_EMAIL;
|
||||||
} else {
|
} else {
|
||||||
$bounceAddress = $from;
|
$bounceAddress = $from;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Strip the human name from the bounce address
|
// Strip the human name from the bounce address
|
||||||
if(preg_match('/^([^<>]*)<([^<>]+)> *$/', $bounceAddress, $parts)) $bounceAddress = $parts[2];
|
if(preg_match('/^([^<>]*)<([^<>]+)> *$/', $bounceAddress, $parts)) $bounceAddress = $parts[2];
|
||||||
|
|
||||||
// $headers["Sender"] = $from;
|
// $headers["Sender"] = $from;
|
||||||
$headers["X-Mailer"] = X_MAILER;
|
$headers["X-Mailer"] = X_MAILER;
|
||||||
@ -350,9 +350,9 @@ class Mailer {
|
|||||||
$file['contents'] = $this->QuotedPrintable_encode($file['contents']);
|
$file['contents'] = $this->QuotedPrintable_encode($file['contents']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$headers = "Content-type: $mimeType;\n\tname=\"$base\"\n".
|
$headers = "Content-type: $mimeType;\n\tname=\"$base\"\n".
|
||||||
"Content-Transfer-Encoding: $encoding\n".
|
"Content-Transfer-Encoding: $encoding\n".
|
||||||
"Content-Disposition: $disposition;\n\tfilename=\"$base\"\n" ;
|
"Content-Disposition: $disposition;\n\tfilename=\"$base\"\n" ;
|
||||||
|
|
||||||
if ( isset($file['contentLocation']) ) $headers .= 'Content-Location: ' . $file['contentLocation'] . "\n" ;
|
if ( isset($file['contentLocation']) ) $headers .= 'Content-Location: ' . $file['contentLocation'] . "\n" ;
|
||||||
|
|
||||||
@ -487,4 +487,4 @@ function validEmailAddr($emailAddress) {
|
|||||||
|
|
||||||
$mailer = Injector::inst()->create('Mailer');
|
$mailer = Injector::inst()->create('Mailer');
|
||||||
return $mailer->validEmailAddr($emailAddress);
|
return $mailer->validEmailAddr($emailAddress);
|
||||||
}
|
}
|
||||||
|
@ -135,9 +135,9 @@ class GDBackend extends Object implements Image_Backend {
|
|||||||
* @todo This method isn't very efficent
|
* @todo This method isn't very efficent
|
||||||
*/
|
*/
|
||||||
public function fittedResize($width, $height) {
|
public function fittedResize($width, $height) {
|
||||||
$gd = $this->resizeByHeight($height);
|
$gd = $this->resizeByHeight($height);
|
||||||
if($gd->width > $width) $gd = $gd->resizeByWidth($width);
|
if($gd->width > $width) $gd = $gd->resizeByWidth($width);
|
||||||
return $gd;
|
return $gd;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -199,9 +199,9 @@ class GDBackend extends Object implements Image_Backend {
|
|||||||
if(!$this->gd) return;
|
if(!$this->gd) return;
|
||||||
|
|
||||||
if(function_exists("imagerotate")) {
|
if(function_exists("imagerotate")) {
|
||||||
$newGD = imagerotate($this->gd, $angle,0);
|
$newGD = imagerotate($this->gd, $angle,0);
|
||||||
} else {
|
} else {
|
||||||
//imagerotate is not included in PHP included in Ubuntu
|
//imagerotate is not included in PHP included in Ubuntu
|
||||||
$newGD = $this->rotatePixelByPixel($angle);
|
$newGD = $this->rotatePixelByPixel($angle);
|
||||||
}
|
}
|
||||||
$output = clone $this;
|
$output = clone $this;
|
||||||
@ -210,45 +210,45 @@ class GDBackend extends Object implements Image_Backend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rotates image by given angle. It's slow because makes it pixel by pixel rather than
|
* Rotates image by given angle. It's slow because makes it pixel by pixel rather than
|
||||||
* using built-in function. Used when imagerotate function is not available(i.e. Ubuntu)
|
* using built-in function. Used when imagerotate function is not available(i.e. Ubuntu)
|
||||||
*
|
*
|
||||||
* @param angle
|
* @param angle
|
||||||
*
|
*
|
||||||
* @return GD
|
* @return GD
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public function rotatePixelByPixel($angle) {
|
public function rotatePixelByPixel($angle) {
|
||||||
$sourceWidth = imagesx($this->gd);
|
$sourceWidth = imagesx($this->gd);
|
||||||
$sourceHeight = imagesy($this->gd);
|
$sourceHeight = imagesy($this->gd);
|
||||||
if ($angle == 180) {
|
if ($angle == 180) {
|
||||||
$destWidth = $sourceWidth;
|
$destWidth = $sourceWidth;
|
||||||
$destHeight = $sourceHeight;
|
$destHeight = $sourceHeight;
|
||||||
} else {
|
} else {
|
||||||
$destWidth = $sourceHeight;
|
$destWidth = $sourceHeight;
|
||||||
$destHeight = $sourceWidth;
|
$destHeight = $sourceWidth;
|
||||||
}
|
}
|
||||||
$rotate=imagecreatetruecolor($destWidth,$destHeight);
|
$rotate=imagecreatetruecolor($destWidth,$destHeight);
|
||||||
imagealphablending($rotate, false);
|
imagealphablending($rotate, false);
|
||||||
for ($x = 0; $x < ($sourceWidth); $x++) {
|
for ($x = 0; $x < ($sourceWidth); $x++) {
|
||||||
for ($y = 0; $y < ($sourceHeight); $y++) {
|
for ($y = 0; $y < ($sourceHeight); $y++) {
|
||||||
$color = imagecolorat($this->gd, $x, $y);
|
$color = imagecolorat($this->gd, $x, $y);
|
||||||
switch ($angle) {
|
switch ($angle) {
|
||||||
case 90:
|
case 90:
|
||||||
imagesetpixel($rotate, $y, $destHeight - $x - 1, $color);
|
imagesetpixel($rotate, $y, $destHeight - $x - 1, $color);
|
||||||
break;
|
break;
|
||||||
case 180:
|
case 180:
|
||||||
imagesetpixel($rotate, $destWidth - $x - 1, $destHeight - $y - 1, $color);
|
imagesetpixel($rotate, $destWidth - $x - 1, $destHeight - $y - 1, $color);
|
||||||
break;
|
break;
|
||||||
case 270:
|
case 270:
|
||||||
imagesetpixel($rotate, $destWidth - $y - 1, $x, $color);
|
imagesetpixel($rotate, $destWidth - $y - 1, $x, $color);
|
||||||
break;
|
break;
|
||||||
default: $rotate = $this->gd;
|
default: $rotate = $this->gd;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return $rotate;
|
return $rotate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -271,7 +271,7 @@ class GDBackend extends Object implements Image_Backend {
|
|||||||
return $output;
|
return $output;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method return width of image.
|
* Method return width of image.
|
||||||
*
|
*
|
||||||
* @return integer width.
|
* @return integer width.
|
||||||
@ -334,9 +334,9 @@ class GDBackend extends Object implements Image_Backend {
|
|||||||
/**
|
/**
|
||||||
* Resize to fit fully within the given box, without resizing. Extra space left around
|
* Resize to fit fully within the given box, without resizing. Extra space left around
|
||||||
* the image will be padded with the background color.
|
* the image will be padded with the background color.
|
||||||
* @param width
|
* @param width
|
||||||
* @param height
|
* @param height
|
||||||
* @param backgroundColour
|
* @param backgroundColour
|
||||||
*/
|
*/
|
||||||
public function paddedResize($width, $height, $backgroundColor = "FFFFFF") {
|
public function paddedResize($width, $height, $backgroundColor = "FFFFFF") {
|
||||||
if(!$this->gd) return;
|
if(!$this->gd) return;
|
||||||
@ -476,4 +476,4 @@ class GD extends GDBackend {
|
|||||||
);
|
);
|
||||||
GDBackend::set_default_quality($quality);
|
GDBackend::set_default_quality($quality);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ class AjaxUniqueTextField extends TextField {
|
|||||||
|
|
||||||
parent::__construct($name, $title, $value);
|
parent::__construct($name, $title, $value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function Field($properties = array()) {
|
public function Field($properties = array()) {
|
||||||
$url = Convert::raw2att( $this->validateURL );
|
$url = Convert::raw2att( $this->validateURL );
|
||||||
|
|
||||||
|
@ -79,7 +79,7 @@ class CheckboxSetField extends OptionsetField {
|
|||||||
if(is_a($object, 'DataObject')) {
|
if(is_a($object, 'DataObject')) {
|
||||||
$items[] = $object->ID;
|
$items[] = $object->ID;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} elseif($values && is_string($values)) {
|
} elseif($values && is_string($values)) {
|
||||||
$items = explode(',', $values);
|
$items = explode(',', $values);
|
||||||
$items = str_replace('{comma}', ',', $items);
|
$items = str_replace('{comma}', ',', $items);
|
||||||
|
@ -163,8 +163,8 @@ class CompositeField extends FormField {
|
|||||||
$formName = (isset($this->form)) ? $this->form->FormName() : '(unknown form)';
|
$formName = (isset($this->form)) ? $this->form->FormName() : '(unknown form)';
|
||||||
if(isset($list[$name])) {
|
if(isset($list[$name])) {
|
||||||
user_error("collateDataFields() I noticed that a field called '$name' appears twice in"
|
user_error("collateDataFields() I noticed that a field called '$name' appears twice in"
|
||||||
. " your form: '{$formName}'. One is a '{$field->class}' and the other is a"
|
. " your form: '{$formName}'. One is a '{$field->class}' and the other is a"
|
||||||
. " '{$list[$name]->class}'", E_USER_ERROR);
|
. " '{$list[$name]->class}'", E_USER_ERROR);
|
||||||
}
|
}
|
||||||
$list[$name] = $field;
|
$list[$name] = $field;
|
||||||
}
|
}
|
||||||
|
@ -52,7 +52,7 @@ class CreditCardField extends TextField {
|
|||||||
if($this->value) foreach($this->value as $part){
|
if($this->value) foreach($this->value as $part){
|
||||||
if(!$part || !(strlen($part) == 4) || !preg_match("/([0-9]{4})/", $part)){
|
if(!$part || !(strlen($part) == 4) || !preg_match("/([0-9]{4})/", $part)){
|
||||||
switch($i){
|
switch($i){
|
||||||
case 0: $number = _t('CreditCardField.FIRST', 'first'); break;
|
case 0: $number = _t('CreditCardField.FIRST', 'first'); break;
|
||||||
case 1: $number = _t('CreditCardField.SECOND', 'second'); break;
|
case 1: $number = _t('CreditCardField.SECOND', 'second'); break;
|
||||||
case 2: $number = _t('CreditCardField.THIRD', 'third'); break;
|
case 2: $number = _t('CreditCardField.THIRD', 'third'); break;
|
||||||
case 3: $number = _t('CreditCardField.FOURTH', 'fourth'); break;
|
case 3: $number = _t('CreditCardField.FOURTH', 'fourth'); break;
|
||||||
|
@ -43,9 +43,9 @@ require_once 'Zend/Date.php';
|
|||||||
*
|
*
|
||||||
* ## Example: German dates with separate fields for day, month, year
|
* ## Example: German dates with separate fields for day, month, year
|
||||||
*
|
*
|
||||||
* $f = new DateField('MyDate');
|
* $f = new DateField('MyDate');
|
||||||
* $f->setLocale('de_DE');
|
* $f->setLocale('de_DE');
|
||||||
* $f->setConfig('dmyfields');
|
* $f->setConfig('dmyfields');
|
||||||
*
|
*
|
||||||
* # Validation
|
* # Validation
|
||||||
*
|
*
|
||||||
@ -314,11 +314,11 @@ class DateField extends TextField {
|
|||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
public static function set_default_config($k, $v) {
|
public static function set_default_config($k, $v) {
|
||||||
if (array_key_exists($k,self::$default_config)) {
|
if (array_key_exists($k,self::$default_config)) {
|
||||||
self::$default_config[$k]=$v;
|
self::$default_config[$k]=$v;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -567,7 +567,7 @@ class DateField_View_JQuery extends Object {
|
|||||||
// Include language files (if required)
|
// Include language files (if required)
|
||||||
if ($this->jqueryLocaleFile){
|
if ($this->jqueryLocaleFile){
|
||||||
Requirements::javascript($this->jqueryLocaleFile);
|
Requirements::javascript($this->jqueryLocaleFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
Requirements::javascript(FRAMEWORK_DIR . "/javascript/DateField.js");
|
Requirements::javascript(FRAMEWORK_DIR . "/javascript/DateField.js");
|
||||||
}
|
}
|
||||||
@ -609,51 +609,51 @@ class DateField_View_JQuery extends Object {
|
|||||||
public static function convert_iso_to_jquery_format($format) {
|
public static function convert_iso_to_jquery_format($format) {
|
||||||
$convert = array(
|
$convert = array(
|
||||||
'/([^d])d([^d])/' => '$1d$2',
|
'/([^d])d([^d])/' => '$1d$2',
|
||||||
'/^d([^d])/' => 'd$1',
|
'/^d([^d])/' => 'd$1',
|
||||||
'/([^d])d$/' => '$1d',
|
'/([^d])d$/' => '$1d',
|
||||||
'/dd/' => 'dd',
|
'/dd/' => 'dd',
|
||||||
'/SS/' => '',
|
'/SS/' => '',
|
||||||
'/eee/' => 'd',
|
'/eee/' => 'd',
|
||||||
'/e/' => 'N',
|
'/e/' => 'N',
|
||||||
'/D/' => '',
|
'/D/' => '',
|
||||||
'/EEEE/' => 'DD',
|
'/EEEE/' => 'DD',
|
||||||
'/EEE/' => 'D',
|
'/EEE/' => 'D',
|
||||||
'/w/' => '',
|
'/w/' => '',
|
||||||
// make single "M" lowercase
|
// make single "M" lowercase
|
||||||
'/([^M])M([^M])/' => '$1m$2',
|
'/([^M])M([^M])/' => '$1m$2',
|
||||||
// make single "M" at start of line lowercase
|
// make single "M" at start of line lowercase
|
||||||
'/^M([^M])/' => 'm$1',
|
'/^M([^M])/' => 'm$1',
|
||||||
// make single "M" at end of line lowercase
|
// make single "M" at end of line lowercase
|
||||||
'/([^M])M$/' => '$1m',
|
'/([^M])M$/' => '$1m',
|
||||||
// match exactly three capital Ms not preceeded or followed by an M
|
// match exactly three capital Ms not preceeded or followed by an M
|
||||||
'/(?<!M)MMM(?!M)/' => 'M',
|
'/(?<!M)MMM(?!M)/' => 'M',
|
||||||
// match exactly two capital Ms not preceeded or followed by an M
|
// match exactly two capital Ms not preceeded or followed by an M
|
||||||
'/(?<!M)MM(?!M)/' => 'mm',
|
'/(?<!M)MM(?!M)/' => 'mm',
|
||||||
// match four capital Ms (maximum allowed)
|
// match four capital Ms (maximum allowed)
|
||||||
'/MMMM/' => 'MM',
|
'/MMMM/' => 'MM',
|
||||||
'/l/' => '',
|
'/l/' => '',
|
||||||
'/YYYY/' => 'yy',
|
'/YYYY/' => 'yy',
|
||||||
'/yyyy/' => 'yy',
|
'/yyyy/' => 'yy',
|
||||||
// See http://open.silverstripe.org/ticket/7669
|
// See http://open.silverstripe.org/ticket/7669
|
||||||
'/y{1,3}/' => 'yy',
|
'/y{1,3}/' => 'yy',
|
||||||
'/a/' => '',
|
'/a/' => '',
|
||||||
'/B/' => '',
|
'/B/' => '',
|
||||||
'/hh/' => '',
|
'/hh/' => '',
|
||||||
'/h/' => '',
|
'/h/' => '',
|
||||||
'/([^H])H([^H])/' => '',
|
'/([^H])H([^H])/' => '',
|
||||||
'/^H([^H])/' => '',
|
'/^H([^H])/' => '',
|
||||||
'/([^H])H$/' => '',
|
'/([^H])H$/' => '',
|
||||||
'/HH/' => '',
|
'/HH/' => '',
|
||||||
// '/mm/' => '',
|
// '/mm/' => '',
|
||||||
'/ss/' => '',
|
'/ss/' => '',
|
||||||
'/zzzz/' => '',
|
'/zzzz/' => '',
|
||||||
'/I/' => '',
|
'/I/' => '',
|
||||||
'/ZZZZ/' => '',
|
'/ZZZZ/' => '',
|
||||||
'/Z/' => '',
|
'/Z/' => '',
|
||||||
'/z/' => '',
|
'/z/' => '',
|
||||||
'/X/' => '',
|
'/X/' => '',
|
||||||
'/r/' => '',
|
'/r/' => '',
|
||||||
'/U/' => '',
|
'/U/' => '',
|
||||||
);
|
);
|
||||||
$patterns = array_keys($convert);
|
$patterns = array_keys($convert);
|
||||||
$replacements = array_values($convert);
|
$replacements = array_values($convert);
|
||||||
|
@ -90,12 +90,12 @@ class FieldGroup extends CompositeField {
|
|||||||
*
|
*
|
||||||
* @param string $zebra one of odd or even.
|
* @param string $zebra one of odd or even.
|
||||||
*/
|
*/
|
||||||
public function setZebra($zebra) {
|
public function setZebra($zebra) {
|
||||||
if($zebra == 'odd' || $zebra == 'even') $this->zebra = $zebra;
|
if($zebra == 'odd' || $zebra == 'even') $this->zebra = $zebra;
|
||||||
else user_error("setZebra passed '$zebra'. It should be passed 'odd' or 'even'", E_USER_WARNING);
|
else user_error("setZebra passed '$zebra'. It should be passed 'odd' or 'even'", E_USER_WARNING);
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
@ -132,4 +132,4 @@ class FieldGroup extends CompositeField {
|
|||||||
public function php($data) {
|
public function php($data) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -999,7 +999,7 @@ class Form extends RequestHandler {
|
|||||||
*
|
*
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
public function validate(){
|
public function validate(){
|
||||||
if($this->validator){
|
if($this->validator){
|
||||||
$errors = $this->validator->validate();
|
$errors = $this->validator->validate();
|
||||||
|
|
||||||
@ -1420,7 +1420,7 @@ class Form extends RequestHandler {
|
|||||||
$result .= "</ul>";
|
$result .= "</ul>";
|
||||||
|
|
||||||
if( $this->validator )
|
if( $this->validator )
|
||||||
$result .= '<h3>'._t('Form.VALIDATOR', 'Validator').'</h3>' . $this->validator->debug();
|
$result .= '<h3>'._t('Form.VALIDATOR', 'Validator').'</h3>' . $this->validator->debug();
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
@ -1437,8 +1437,8 @@ class Form extends RequestHandler {
|
|||||||
*/
|
*/
|
||||||
public function testSubmission($action, $data) {
|
public function testSubmission($action, $data) {
|
||||||
$data['action_' . $action] = true;
|
$data['action_' . $action] = true;
|
||||||
|
|
||||||
return Director::test($this->FormAction(), $data, Controller::curr()->getSession());
|
return Director::test($this->FormAction(), $data, Controller::curr()->getSession());
|
||||||
|
|
||||||
//$response = $this->controller->run($data);
|
//$response = $this->controller->run($data);
|
||||||
//return $response;
|
//return $response;
|
||||||
|
@ -85,7 +85,7 @@ class FormField extends RequestHandler {
|
|||||||
protected
|
protected
|
||||||
$template,
|
$template,
|
||||||
$fieldHolderTemplate,
|
$fieldHolderTemplate,
|
||||||
$smallFieldHolderTemplate;
|
$smallFieldHolderTemplate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var array All attributes on the form field (not the field holder).
|
* @var array All attributes on the form field (not the field holder).
|
||||||
@ -582,14 +582,14 @@ class FormField extends RequestHandler {
|
|||||||
return $obj->renderWith($this->getFieldHolderTemplates());
|
return $obj->renderWith($this->getFieldHolderTemplates());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a restricted field holder used within things like FieldGroups.
|
* Returns a restricted field holder used within things like FieldGroups.
|
||||||
*
|
*
|
||||||
* @param array $properties
|
* @param array $properties
|
||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function SmallFieldHolder($properties = array()) {
|
public function SmallFieldHolder($properties = array()) {
|
||||||
$obj = ($properties) ? $this->customise($properties) : $this;
|
$obj = ($properties) ? $this->customise($properties) : $this;
|
||||||
|
|
||||||
return $obj->renderWith($this->getSmallFieldHolderTemplates());
|
return $obj->renderWith($this->getSmallFieldHolderTemplates());
|
||||||
|
@ -175,8 +175,8 @@ class HtmlEditorField extends TextareaField {
|
|||||||
// Save file & link tracking data.
|
// Save file & link tracking data.
|
||||||
if(class_exists('SiteTree')) {
|
if(class_exists('SiteTree')) {
|
||||||
if($record->ID && $record->many_many('LinkTracking') && $tracker = $record->LinkTracking()) {
|
if($record->ID && $record->many_many('LinkTracking') && $tracker = $record->LinkTracking()) {
|
||||||
$tracker->removeByFilter(sprintf('"FieldName" = \'%s\' AND "SiteTreeID" = %d',
|
$tracker->removeByFilter(sprintf('"FieldName" = \'%s\' AND "SiteTreeID" = %d',
|
||||||
$this->name, $record->ID));
|
$this->name, $record->ID));
|
||||||
|
|
||||||
if($linkedPages) foreach($linkedPages as $item) {
|
if($linkedPages) foreach($linkedPages as $item) {
|
||||||
$SQL_fieldName = Convert::raw2sql($this->name);
|
$SQL_fieldName = Convert::raw2sql($this->name);
|
||||||
@ -186,9 +186,9 @@ class HtmlEditorField extends TextareaField {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if($record->ID && $record->many_many('ImageTracking') && $tracker = $record->ImageTracking()) {
|
if($record->ID && $record->many_many('ImageTracking') && $tracker = $record->ImageTracking()) {
|
||||||
$tracker->where(
|
$tracker->where(
|
||||||
sprintf('"FieldName" = \'%s\' AND "SiteTreeID" = %d', $this->name, $record->ID)
|
sprintf('"FieldName" = \'%s\' AND "SiteTreeID" = %d', $this->name, $record->ID)
|
||||||
)->removeAll();
|
)->removeAll();
|
||||||
|
|
||||||
$fieldName = $this->name;
|
$fieldName = $this->name;
|
||||||
if($linkedFiles) foreach($linkedFiles as $item) {
|
if($linkedFiles) foreach($linkedFiles as $item) {
|
||||||
|
@ -14,8 +14,8 @@ class NumericField extends TextField{
|
|||||||
/** PHP Validation **/
|
/** PHP Validation **/
|
||||||
public function validate($validator){
|
public function validate($validator){
|
||||||
if($this->value && !is_numeric(trim($this->value))){
|
if($this->value && !is_numeric(trim($this->value))){
|
||||||
$validator->validationError(
|
$validator->validationError(
|
||||||
$this->name,
|
$this->name,
|
||||||
_t(
|
_t(
|
||||||
'NumericField.VALIDATION', "'{value}' is not a number, only numbers can be accepted for this field",
|
'NumericField.VALIDATION', "'{value}' is not a number, only numbers can be accepted for this field",
|
||||||
array('value' => $this->value)
|
array('value' => $this->value)
|
||||||
|
@ -33,11 +33,11 @@ class PhoneNumberField extends FormField {
|
|||||||
list($countryCode, $areaCode, $phoneNumber, $extension) = $this->parseValue();
|
list($countryCode, $areaCode, $phoneNumber, $extension) = $this->parseValue();
|
||||||
$hasTitle = false;
|
$hasTitle = false;
|
||||||
|
|
||||||
if ($this->value=="") {
|
if ($this->value=="") {
|
||||||
$countryCode=$this->countryCode;
|
$countryCode=$this->countryCode;
|
||||||
$areaCode=$this->areaCode;
|
$areaCode=$this->areaCode;
|
||||||
$extension=$this->ext;
|
$extension=$this->ext;
|
||||||
}
|
}
|
||||||
|
|
||||||
if($this->countryCode !== null) {
|
if($this->countryCode !== null) {
|
||||||
$fields->push(new NumericField($this->name.'[Country]', '+', $countryCode, 4));
|
$fields->push(new NumericField($this->name.'[Country]', '+', $countryCode, 4));
|
||||||
@ -94,7 +94,7 @@ class PhoneNumberField extends FormField {
|
|||||||
preg_match( '/^(?:(?:\+(\d+))?\s*\((\d+)\))?\s*([0-9A-Za-z]*)\s*(?:[#]\s*(\d+))?$/', $this->value, $parts);
|
preg_match( '/^(?:(?:\+(\d+))?\s*\((\d+)\))?\s*([0-9A-Za-z]*)\s*(?:[#]\s*(\d+))?$/', $this->value, $parts);
|
||||||
else
|
else
|
||||||
return array( '', '', $this->value, '' );
|
return array( '', '', $this->value, '' );
|
||||||
|
|
||||||
if(is_array($parts)) array_shift( $parts );
|
if(is_array($parts)) array_shift( $parts );
|
||||||
|
|
||||||
for ($x=0;$x<=3;$x++) {
|
for ($x=0;$x<=3;$x++) {
|
||||||
|
@ -42,15 +42,15 @@ class RequiredFields extends Validator {
|
|||||||
* Debug helper
|
* Debug helper
|
||||||
*/
|
*/
|
||||||
public function debug() {
|
public function debug() {
|
||||||
if(!is_array($this->required)) return false;
|
if(!is_array($this->required)) return false;
|
||||||
|
|
||||||
$result = "<ul>";
|
$result = "<ul>";
|
||||||
foreach( $this->required as $name ){
|
foreach( $this->required as $name ){
|
||||||
$result .= "<li>$name</li>";
|
$result .= "<li>$name</li>";
|
||||||
}
|
}
|
||||||
|
|
||||||
$result .= "</ul>";
|
$result .= "</ul>";
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -211,7 +211,7 @@ class GridField extends FormField {
|
|||||||
public function getManipulatedList() {
|
public function getManipulatedList() {
|
||||||
$list = $this->getList();
|
$list = $this->getList();
|
||||||
foreach($this->getComponents() as $item) {
|
foreach($this->getComponents() as $item) {
|
||||||
if($item instanceof GridField_DataManipulator) {
|
if($item instanceof GridField_DataManipulator) {
|
||||||
$list = $item->getManipulatedData($this, $list);
|
$list = $item->getManipulatedData($this, $list);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -244,7 +244,6 @@
|
|||||||
this._super();
|
this._super();
|
||||||
this.selectable('destroy');
|
this.selectable('destroy');
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -6,15 +6,15 @@
|
|||||||
* ajax / iframe submissions
|
* ajax / iframe submissions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var ss = ss || {};
|
var ss = ss || {};
|
||||||
/**
|
/**
|
||||||
* Wrapper for HTML WYSIWYG libraries, which abstracts library internals
|
* Wrapper for HTML WYSIWYG libraries, which abstracts library internals
|
||||||
* from interface concerns like inserting and editing links.
|
* from interface concerns like inserting and editing links.
|
||||||
* Caution: Incomplete and unstable API.
|
* Caution: Incomplete and unstable API.
|
||||||
*/
|
*/
|
||||||
ss.editorWrappers = {};
|
ss.editorWrappers = {};
|
||||||
ss.editorWrappers.initial
|
ss.editorWrappers.initial
|
||||||
ss.editorWrappers.tinyMCE = (function() {
|
ss.editorWrappers.tinyMCE = (function() {
|
||||||
return {
|
return {
|
||||||
init: function(config) {
|
init: function(config) {
|
||||||
if(!ss.editorWrappers.tinyMCE.initialized) {
|
if(!ss.editorWrappers.tinyMCE.initialized) {
|
||||||
@ -807,7 +807,7 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
|
|||||||
if(header) header[(hasItems) ? 'show' : 'hide']();
|
if(header) header[(hasItems) ? 'show' : 'hide']();
|
||||||
|
|
||||||
// Disable "insert" button if no files are selected
|
// Disable "insert" button if no files are selected
|
||||||
this.find('.Actions :submit')
|
this.find('.Actions :submit')
|
||||||
.button(hasItems ? 'enable' : 'disable')
|
.button(hasItems ? 'enable' : 'disable')
|
||||||
.toggleClass('ui-state-disabled', !hasItems);
|
.toggleClass('ui-state-disabled', !hasItems);
|
||||||
|
|
||||||
|
@ -108,7 +108,7 @@ ss.i18n = {
|
|||||||
* @return string result : Stripped string
|
* @return string result : Stripped string
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
stripStr: function(str) {
|
stripStr: function(str) {
|
||||||
return str.replace(/^\s*/, "").replace(/\s*$/, "");
|
return str.replace(/^\s*/, "").replace(/\s*$/, "");
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -228,4 +228,4 @@ ss.i18n = {
|
|||||||
|
|
||||||
ss.i18n.addEvent(window, "load", function() {
|
ss.i18n.addEvent(window, "load", function() {
|
||||||
ss.i18n.init();
|
ss.i18n.init();
|
||||||
});
|
});
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
|
|
||||||
// loaded files list - to protect against loading existed file again (by PGA)
|
// loaded files list - to protect against loading existed file again (by PGA)
|
||||||
_ondemand_loaded_list : null,
|
_ondemand_loaded_list : null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the given CSS or JS script has already been loaded
|
* Returns true if the given CSS or JS script has already been loaded
|
||||||
*/
|
*/
|
||||||
@ -160,4 +160,4 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
})(jQuery);
|
})(jQuery);
|
||||||
|
@ -212,7 +212,7 @@ Tree.prototype = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update the helper classes accordingly
|
// Update the helper classes accordingly
|
||||||
if(!hasChildren) this.removeNodeClass('children');
|
if(!hasChildren) this.removeNodeClass('children');
|
||||||
else this.lastTreeNode().addNodeClass('last');
|
else this.lastTreeNode().addNodeClass('last');
|
||||||
|
|
||||||
// Update the helper variables
|
// Update the helper variables
|
||||||
@ -303,8 +303,8 @@ TreeNode.prototype = {
|
|||||||
|
|
||||||
// Move all the nodes up until that point into spanC
|
// Move all the nodes up until that point into spanC
|
||||||
for(j=startingPoint;j<stoppingPoint;j++) {
|
for(j=startingPoint;j<stoppingPoint;j++) {
|
||||||
/* Use [startingPoint] every time, because the appentChild
|
/* Use [startingPoint] every time, because the appentChild
|
||||||
removes the node, so it then points to the next one. */
|
removes the node, so it then points to the next one. */
|
||||||
spanC.appendChild(li.childNodes[startingPoint]);
|
spanC.appendChild(li.childNodes[startingPoint]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -527,7 +527,7 @@ TreeNode.prototype = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update the helper classes accordingly
|
// Update the helper classes accordingly
|
||||||
if(!hasChildren) this.removeNodeClass('children');
|
if(!hasChildren) this.removeNodeClass('children');
|
||||||
else this.lastTreeNode().addNodeClass('last');
|
else this.lastTreeNode().addNodeClass('last');
|
||||||
|
|
||||||
// Update the helper variables
|
// Update the helper variables
|
||||||
@ -599,27 +599,27 @@ TreeNode.prototype = {
|
|||||||
|
|
||||||
/* Close or Open all the trees, at beginning or on request. sjd. */
|
/* Close or Open all the trees, at beginning or on request. sjd. */
|
||||||
function treeCloseAll() {
|
function treeCloseAll() {
|
||||||
var candidates = document.getElementsByTagName('li');
|
var candidates = document.getElementsByTagName('li');
|
||||||
for (var i=0;i<candidates.length;i++) {
|
for (var i=0;i<candidates.length;i++) {
|
||||||
var aSpan = candidates[i].childNodes[0];
|
var aSpan = candidates[i].childNodes[0];
|
||||||
if(aSpan.childNodes[0] && aSpan.childNodes[0].className == "b") {
|
if(aSpan.childNodes[0] && aSpan.childNodes[0].className == "b") {
|
||||||
if (!aSpan.className.match(/spanClosed/) && candidates[i].id != 'record-0' ) {
|
if (!aSpan.className.match(/spanClosed/) && candidates[i].id != 'record-0' ) {
|
||||||
aSpan.childNodes[0].onclick();
|
aSpan.childNodes[0].onclick();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function treeOpenAll() {
|
function treeOpenAll() {
|
||||||
var candidates = document.getElementsByTagName('li');
|
var candidates = document.getElementsByTagName('li');
|
||||||
for (var i=0;i<candidates.length;i++) {
|
for (var i=0;i<candidates.length;i++) {
|
||||||
var aSpan = candidates[i].childNodes[0];
|
var aSpan = candidates[i].childNodes[0];
|
||||||
if(aSpan.childNodes[0] && aSpan.childNodes[0].className == "b") {
|
if(aSpan.childNodes[0] && aSpan.childNodes[0].className == "b") {
|
||||||
if (aSpan.className.match(/spanClosed/)) {
|
if (aSpan.className.match(/spanClosed/)) {
|
||||||
aSpan.childNodes[0].onclick();
|
aSpan.childNodes[0].onclick();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -922,4 +922,4 @@ MultiselectTree.prototype = {
|
|||||||
delete this.selectedNodes[idx];
|
delete this.selectedNodes[idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -218,7 +218,7 @@ class DataList extends ViewableData implements SS_List, SS_Filterable, SS_Sortab
|
|||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
public function canSortBy($fieldName) {
|
public function canSortBy($fieldName) {
|
||||||
return $this->dataQuery()->query()->canSortBy($fieldName);
|
return $this->dataQuery()->query()->canSortBy($fieldName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -718,7 +718,7 @@ class DataList extends ViewableData implements SS_List, SS_Filterable, SS_Sortab
|
|||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
public function max($fieldName) {
|
public function max($fieldName) {
|
||||||
return $this->dataQuery->max($fieldName);
|
return $this->dataQuery->max($fieldName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -728,7 +728,7 @@ class DataList extends ViewableData implements SS_List, SS_Filterable, SS_Sortab
|
|||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
public function min($fieldName) {
|
public function min($fieldName) {
|
||||||
return $this->dataQuery->min($fieldName);
|
return $this->dataQuery->min($fieldName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -738,7 +738,7 @@ class DataList extends ViewableData implements SS_List, SS_Filterable, SS_Sortab
|
|||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
public function avg($fieldName) {
|
public function avg($fieldName) {
|
||||||
return $this->dataQuery->avg($fieldName);
|
return $this->dataQuery->avg($fieldName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -748,7 +748,7 @@ class DataList extends ViewableData implements SS_List, SS_Filterable, SS_Sortab
|
|||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
public function sum($fieldName) {
|
public function sum($fieldName) {
|
||||||
return $this->dataQuery->sum($fieldName);
|
return $this->dataQuery->sum($fieldName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -878,7 +878,7 @@ class DataList extends ViewableData implements SS_List, SS_Filterable, SS_Sortab
|
|||||||
|
|
||||||
// Index current data
|
// Index current data
|
||||||
foreach($this->column() as $id) {
|
foreach($this->column() as $id) {
|
||||||
$has[$id] = true;
|
$has[$id] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keep track of items to delete
|
// Keep track of items to delete
|
||||||
@ -996,7 +996,7 @@ class DataList extends ViewableData implements SS_List, SS_Filterable, SS_Sortab
|
|||||||
*/
|
*/
|
||||||
public function newObject($initialFields = null) {
|
public function newObject($initialFields = null) {
|
||||||
$class = $this->dataClass;
|
$class = $this->dataClass;
|
||||||
return Injector::inst()->create($class, $initialFields, false, $this->model);
|
return Injector::inst()->create($class, $initialFields, false, $this->model);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1012,14 +1012,14 @@ class DataList extends ViewableData implements SS_List, SS_Filterable, SS_Sortab
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove an item from this DataList by ID
|
* Remove an item from this DataList by ID
|
||||||
*
|
*
|
||||||
* @param int $itemID - The primary ID
|
* @param int $itemID - The primary ID
|
||||||
*/
|
*/
|
||||||
public function removeByID($itemID) {
|
public function removeByID($itemID) {
|
||||||
$item = $this->byID($itemID);
|
$item = $this->byID($itemID);
|
||||||
if($item) return $item->delete();
|
if($item) return $item->delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1091,7 +1091,7 @@ class DataList extends ViewableData implements SS_List, SS_Filterable, SS_Sortab
|
|||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function offsetExists($key) {
|
public function offsetExists($key) {
|
||||||
return ($this->limit(1,$key)->First() != null);
|
return ($this->limit(1,$key)->First() != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1101,7 +1101,7 @@ class DataList extends ViewableData implements SS_List, SS_Filterable, SS_Sortab
|
|||||||
* @return DataObject
|
* @return DataObject
|
||||||
*/
|
*/
|
||||||
public function offsetGet($key) {
|
public function offsetGet($key) {
|
||||||
return $this->limit(1, $key)->First();
|
return $this->limit(1, $key)->First();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1111,7 +1111,7 @@ class DataList extends ViewableData implements SS_List, SS_Filterable, SS_Sortab
|
|||||||
* @param mixed $value
|
* @param mixed $value
|
||||||
*/
|
*/
|
||||||
public function offsetSet($key, $value) {
|
public function offsetSet($key, $value) {
|
||||||
user_error("Can't alter items in a DataList using array-access", E_USER_ERROR);
|
user_error("Can't alter items in a DataList using array-access", E_USER_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1120,7 +1120,7 @@ class DataList extends ViewableData implements SS_List, SS_Filterable, SS_Sortab
|
|||||||
* @param mixed $key
|
* @param mixed $key
|
||||||
*/
|
*/
|
||||||
public function offsetUnset($key) {
|
public function offsetUnset($key) {
|
||||||
user_error("Can't alter items in a DataList using array-access", E_USER_ERROR);
|
user_error("Can't alter items in a DataList using array-access", E_USER_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -13,46 +13,46 @@
|
|||||||
*
|
*
|
||||||
* <code>
|
* <code>
|
||||||
* class Article extends DataObject implements PermissionProvider {
|
* class Article extends DataObject implements PermissionProvider {
|
||||||
* static $api_access = true;
|
* static $api_access = true;
|
||||||
*
|
*
|
||||||
* function canView($member = false) {
|
* function canView($member = false) {
|
||||||
* return Permission::check('ARTICLE_VIEW');
|
* return Permission::check('ARTICLE_VIEW');
|
||||||
* }
|
* }
|
||||||
* function canEdit($member = false) {
|
* function canEdit($member = false) {
|
||||||
* return Permission::check('ARTICLE_EDIT');
|
* return Permission::check('ARTICLE_EDIT');
|
||||||
* }
|
* }
|
||||||
* function canDelete() {
|
* function canDelete() {
|
||||||
* return Permission::check('ARTICLE_DELETE');
|
* return Permission::check('ARTICLE_DELETE');
|
||||||
* }
|
* }
|
||||||
* function canCreate() {
|
* function canCreate() {
|
||||||
* return Permission::check('ARTICLE_CREATE');
|
* return Permission::check('ARTICLE_CREATE');
|
||||||
* }
|
* }
|
||||||
* function providePermissions() {
|
* function providePermissions() {
|
||||||
* return array(
|
* return array(
|
||||||
* 'ARTICLE_VIEW' => 'Read an article object',
|
* 'ARTICLE_VIEW' => 'Read an article object',
|
||||||
* 'ARTICLE_EDIT' => 'Edit an article object',
|
* 'ARTICLE_EDIT' => 'Edit an article object',
|
||||||
* 'ARTICLE_DELETE' => 'Delete an article object',
|
* 'ARTICLE_DELETE' => 'Delete an article object',
|
||||||
* 'ARTICLE_CREATE' => 'Create an article object',
|
* 'ARTICLE_CREATE' => 'Create an article object',
|
||||||
* );
|
* );
|
||||||
* }
|
* }
|
||||||
* }
|
* }
|
||||||
* </code>
|
* </code>
|
||||||
*
|
*
|
||||||
* Object-level access control by {@link Group} membership:
|
* Object-level access control by {@link Group} membership:
|
||||||
* <code>
|
* <code>
|
||||||
* class Article extends DataObject {
|
* class Article extends DataObject {
|
||||||
* static $api_access = true;
|
* static $api_access = true;
|
||||||
*
|
*
|
||||||
* function canView($member = false) {
|
* function canView($member = false) {
|
||||||
* if(!$member) $member = Member::currentUser();
|
* if(!$member) $member = Member::currentUser();
|
||||||
* return $member->inGroup('Subscribers');
|
* return $member->inGroup('Subscribers');
|
||||||
* }
|
* }
|
||||||
* function canEdit($member = false) {
|
* function canEdit($member = false) {
|
||||||
* if(!$member) $member = Member::currentUser();
|
* if(!$member) $member = Member::currentUser();
|
||||||
* return $member->inGroup('Editors');
|
* return $member->inGroup('Editors');
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* // ...
|
* // ...
|
||||||
* }
|
* }
|
||||||
* </code>
|
* </code>
|
||||||
*
|
*
|
||||||
@ -818,7 +818,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
|
|||||||
* @param $priority String left|right Determines who wins in case of a conflict (optional)
|
* @param $priority String left|right Determines who wins in case of a conflict (optional)
|
||||||
* @param $includeRelations Boolean Merge any existing relations (optional)
|
* @param $includeRelations Boolean Merge any existing relations (optional)
|
||||||
* @param $overwriteWithEmpty Boolean Overwrite existing left values with empty right values.
|
* @param $overwriteWithEmpty Boolean Overwrite existing left values with empty right values.
|
||||||
* Only applicable with $priority='right'. (optional)
|
* Only applicable with $priority='right'. (optional)
|
||||||
* @return Boolean
|
* @return Boolean
|
||||||
*/
|
*/
|
||||||
public function merge($rightObj, $priority = 'right', $includeRelations = true, $overwriteWithEmpty = false) {
|
public function merge($rightObj, $priority = 'right', $includeRelations = true, $overwriteWithEmpty = false) {
|
||||||
@ -997,7 +997,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
|
|||||||
|
|
||||||
if($defaults && !is_array($defaults)) {
|
if($defaults && !is_array($defaults)) {
|
||||||
user_error("Bad '$this->class' defaults given: " . var_export($defaults, true),
|
user_error("Bad '$this->class' defaults given: " . var_export($defaults, true),
|
||||||
E_USER_WARNING);
|
E_USER_WARNING);
|
||||||
$defaults = null;
|
$defaults = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1258,8 +1258,8 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
|
|||||||
. " Make sure that you call parent::onBeforeDelete().", E_USER_ERROR);
|
. " Make sure that you call parent::onBeforeDelete().", E_USER_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deleting a record without an ID shouldn't do anything
|
// Deleting a record without an ID shouldn't do anything
|
||||||
if(!$this->ID) throw new LogicException("DataObject::delete() called on a DataObject without an ID");
|
if(!$this->ID) throw new LogicException("DataObject::delete() called on a DataObject without an ID");
|
||||||
|
|
||||||
// TODO: This is quite ugly. To improve:
|
// TODO: This is quite ugly. To improve:
|
||||||
// - move the details of the delete code in the DataQuery system
|
// - move the details of the delete code in the DataQuery system
|
||||||
@ -1868,8 +1868,8 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
|
|||||||
* Used by {@link SearchContext}.
|
* Used by {@link SearchContext}.
|
||||||
*
|
*
|
||||||
* @param array $_params
|
* @param array $_params
|
||||||
* 'fieldClasses': Associative array of field names as keys and FormField classes as values
|
* 'fieldClasses': Associative array of field names as keys and FormField classes as values
|
||||||
* 'restrictFields': Numeric array of a field name whitelist
|
* 'restrictFields': Numeric array of a field name whitelist
|
||||||
* @return FieldList
|
* @return FieldList
|
||||||
*/
|
*/
|
||||||
public function scaffoldSearchFields($_params = null) {
|
public function scaffoldSearchFields($_params = null) {
|
||||||
@ -1960,14 +1960,14 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
|
|||||||
* or extended onto it by using {@link DataExtension->updateCMSFields()}.
|
* or extended onto it by using {@link DataExtension->updateCMSFields()}.
|
||||||
*
|
*
|
||||||
* <code>
|
* <code>
|
||||||
* klass MyCustomClass extends DataObject {
|
* class MyCustomClass extends DataObject {
|
||||||
* static $db = array('CustomProperty'=>'Boolean');
|
* static $db = array('CustomProperty'=>'Boolean');
|
||||||
*
|
*
|
||||||
* function getCMSFields() {
|
* function getCMSFields() {
|
||||||
* $fields = parent::getCMSFields();
|
* $fields = parent::getCMSFields();
|
||||||
* $fields->addFieldToTab('Root.Content',new CheckboxField('CustomProperty'));
|
* $fields->addFieldToTab('Root.Content',new CheckboxField('CustomProperty'));
|
||||||
* return $fields;
|
* return $fields;
|
||||||
* }
|
* }
|
||||||
* }
|
* }
|
||||||
* </code>
|
* </code>
|
||||||
*
|
*
|
||||||
@ -3130,8 +3130,8 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
|
|||||||
// var_dump("{$ancestorClass}.{$type}_{$name}");
|
// var_dump("{$ancestorClass}.{$type}_{$name}");
|
||||||
$autoLabels[$name] = _t("{$ancestorClass}.{$type}_{$name}",FormField::name_to_label($name));
|
$autoLabels[$name] = _t("{$ancestorClass}.{$type}_{$name}",FormField::name_to_label($name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$labels = array_merge((array)$autoLabels, (array)$customLabels);
|
$labels = array_merge((array)$autoLabels, (array)$customLabels);
|
||||||
$this->extend('updateFieldLabels', $labels);
|
$this->extend('updateFieldLabels', $labels);
|
||||||
@ -3278,7 +3278,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
|
|||||||
*
|
*
|
||||||
* <code>
|
* <code>
|
||||||
* array(
|
* array(
|
||||||
* 'MySQLDatabase' => 'ENGINE=MyISAM'
|
* 'MySQLDatabase' => 'ENGINE=MyISAM'
|
||||||
* )
|
* )
|
||||||
* </code>
|
* </code>
|
||||||
*
|
*
|
||||||
@ -3321,8 +3321,8 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
|
|||||||
*
|
*
|
||||||
* Example:
|
* Example:
|
||||||
* array(
|
* array(
|
||||||
* array('Title' => "DefaultPage1", 'PageTitle' => 'page1'),
|
* array('Title' => "DefaultPage1", 'PageTitle' => 'page1'),
|
||||||
* array('Title' => "DefaultPage2")
|
* array('Title' => "DefaultPage2")
|
||||||
* ).
|
* ).
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
@ -3378,7 +3378,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
|
|||||||
* Example code:
|
* Example code:
|
||||||
* <code>
|
* <code>
|
||||||
* public static $many_many_extraFields = array(
|
* public static $many_many_extraFields = array(
|
||||||
* 'Members' => array(
|
* 'Members' => array(
|
||||||
* 'Role' => 'Varchar(100)'
|
* 'Role' => 'Varchar(100)'
|
||||||
* )
|
* )
|
||||||
* );
|
* );
|
||||||
@ -3408,8 +3408,8 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
|
|||||||
*
|
*
|
||||||
* Overriding the default filter, with a custom defined filter:
|
* Overriding the default filter, with a custom defined filter:
|
||||||
* <code>
|
* <code>
|
||||||
* static $searchable_fields = array(
|
* static $searchable_fields = array(
|
||||||
* "Name" => "PartialMatchFilter"
|
* "Name" => "PartialMatchFilter"
|
||||||
* );
|
* );
|
||||||
* </code>
|
* </code>
|
||||||
*
|
*
|
||||||
@ -3417,21 +3417,21 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
|
|||||||
* The 'filter' parameter will be generated from {@link DBField::$default_search_filter_class}.
|
* The 'filter' parameter will be generated from {@link DBField::$default_search_filter_class}.
|
||||||
* The 'title' parameter will be generated from {@link DataObject->fieldLabels()}.
|
* The 'title' parameter will be generated from {@link DataObject->fieldLabels()}.
|
||||||
* <code>
|
* <code>
|
||||||
* static $searchable_fields = array(
|
* static $searchable_fields = array(
|
||||||
* "Name" => array(
|
* "Name" => array(
|
||||||
* "field" => "TextField"
|
* "field" => "TextField"
|
||||||
* )
|
* )
|
||||||
* );
|
* );
|
||||||
* </code>
|
* </code>
|
||||||
*
|
*
|
||||||
* Overriding the default form field, filter and title:
|
* Overriding the default form field, filter and title:
|
||||||
* <code>
|
* <code>
|
||||||
* static $searchable_fields = array(
|
* static $searchable_fields = array(
|
||||||
* "Organisation.ZipCode" => array(
|
* "Organisation.ZipCode" => array(
|
||||||
* "field" => "TextField",
|
* "field" => "TextField",
|
||||||
* "filter" => "PartialMatchFilter",
|
* "filter" => "PartialMatchFilter",
|
||||||
* "title" => 'Organisation ZIP'
|
* "title" => 'Organisation ZIP'
|
||||||
* )
|
* )
|
||||||
* );
|
* );
|
||||||
* </code>
|
* </code>
|
||||||
*/
|
*/
|
||||||
@ -3483,21 +3483,21 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the given method/parameter has a value
|
* Returns true if the given method/parameter has a value
|
||||||
* (Uses the DBField::hasValue if the parameter is a database field)
|
* (Uses the DBField::hasValue if the parameter is a database field)
|
||||||
*
|
*
|
||||||
* @param string $field The field name
|
* @param string $field The field name
|
||||||
* @param array $arguments
|
* @param array $arguments
|
||||||
* @param bool $cache
|
* @param bool $cache
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
public function hasValue($field, $arguments = null, $cache = true) {
|
public function hasValue($field, $arguments = null, $cache = true) {
|
||||||
$obj = $this->dbObject($field);
|
$obj = $this->dbObject($field);
|
||||||
if($obj) {
|
if($obj) {
|
||||||
return $obj->exists();
|
return $obj->exists();
|
||||||
} else {
|
} else {
|
||||||
return parent::hasValue($field, $arguments, $cache);
|
return parent::hasValue($field, $arguments, $cache);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -338,7 +338,7 @@ class DataQuery {
|
|||||||
* @param String $field Unquoted database column name (will be escaped automatically)
|
* @param String $field Unquoted database column name (will be escaped automatically)
|
||||||
*/
|
*/
|
||||||
public function max($field) {
|
public function max($field) {
|
||||||
return $this->aggregate(sprintf('MAX("%s")', Convert::raw2sql($field)));
|
return $this->aggregate(sprintf('MAX("%s")', Convert::raw2sql($field)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -347,7 +347,7 @@ class DataQuery {
|
|||||||
* @param String $field Unquoted database column name (will be escaped automatically)
|
* @param String $field Unquoted database column name (will be escaped automatically)
|
||||||
*/
|
*/
|
||||||
public function min($field) {
|
public function min($field) {
|
||||||
return $this->aggregate(sprintf('MIN("%s")', Convert::raw2sql($field)));
|
return $this->aggregate(sprintf('MIN("%s")', Convert::raw2sql($field)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -356,7 +356,7 @@ class DataQuery {
|
|||||||
* @param String $field Unquoted database column name (will be escaped automatically)
|
* @param String $field Unquoted database column name (will be escaped automatically)
|
||||||
*/
|
*/
|
||||||
public function avg($field) {
|
public function avg($field) {
|
||||||
return $this->aggregate(sprintf('AVG("%s")', Convert::raw2sql($field)));
|
return $this->aggregate(sprintf('AVG("%s")', Convert::raw2sql($field)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -365,14 +365,14 @@ class DataQuery {
|
|||||||
* @param String $field Unquoted database column name (will be escaped automatically)
|
* @param String $field Unquoted database column name (will be escaped automatically)
|
||||||
*/
|
*/
|
||||||
public function sum($field) {
|
public function sum($field) {
|
||||||
return $this->aggregate(sprintf('SUM("%s")', Convert::raw2sql($field)));
|
return $this->aggregate(sprintf('SUM("%s")', Convert::raw2sql($field)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Runs a raw aggregate expression. Please handle escaping yourself
|
* Runs a raw aggregate expression. Please handle escaping yourself
|
||||||
*/
|
*/
|
||||||
public function aggregate($expression) {
|
public function aggregate($expression) {
|
||||||
return $this->getFinalisedQuery()->aggregate($expression)->execute()->value();
|
return $this->getFinalisedQuery()->aggregate($expression)->execute()->value();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -567,74 +567,74 @@ class DataQuery {
|
|||||||
* @return The model class of the related item
|
* @return The model class of the related item
|
||||||
*/
|
*/
|
||||||
public function applyRelation($relation) {
|
public function applyRelation($relation) {
|
||||||
// NO-OP
|
// NO-OP
|
||||||
if(!$relation) return $this->dataClass;
|
if(!$relation) return $this->dataClass;
|
||||||
|
|
||||||
if(is_string($relation)) $relation = explode(".", $relation);
|
if(is_string($relation)) $relation = explode(".", $relation);
|
||||||
|
|
||||||
$modelClass = $this->dataClass;
|
$modelClass = $this->dataClass;
|
||||||
|
|
||||||
foreach($relation as $rel) {
|
foreach($relation as $rel) {
|
||||||
$model = singleton($modelClass);
|
$model = singleton($modelClass);
|
||||||
if ($component = $model->has_one($rel)) {
|
if ($component = $model->has_one($rel)) {
|
||||||
if(!$this->query->isJoinedTo($component)) {
|
if(!$this->query->isJoinedTo($component)) {
|
||||||
$foreignKey = $model->getReverseAssociation($component);
|
$foreignKey = $model->getReverseAssociation($component);
|
||||||
$this->query->addLeftJoin($component,
|
$this->query->addLeftJoin($component,
|
||||||
"\"$component\".\"ID\" = \"{$modelClass}\".\"{$foreignKey}ID\"");
|
"\"$component\".\"ID\" = \"{$modelClass}\".\"{$foreignKey}ID\"");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* add join clause to the component's ancestry classes so that the search filter could search on
|
* add join clause to the component's ancestry classes so that the search filter could search on
|
||||||
* its ancestor fields.
|
* its ancestor fields.
|
||||||
*/
|
*/
|
||||||
$ancestry = ClassInfo::ancestry($component, true);
|
$ancestry = ClassInfo::ancestry($component, true);
|
||||||
if(!empty($ancestry)){
|
if(!empty($ancestry)){
|
||||||
$ancestry = array_reverse($ancestry);
|
$ancestry = array_reverse($ancestry);
|
||||||
foreach($ancestry as $ancestor){
|
foreach($ancestry as $ancestor){
|
||||||
if($ancestor != $component){
|
if($ancestor != $component){
|
||||||
$this->query->addInnerJoin($ancestor, "\"$component\".\"ID\" = \"$ancestor\".\"ID\"");
|
$this->query->addInnerJoin($ancestor, "\"$component\".\"ID\" = \"$ancestor\".\"ID\"");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$modelClass = $component;
|
$modelClass = $component;
|
||||||
|
|
||||||
} elseif ($component = $model->has_many($rel)) {
|
} elseif ($component = $model->has_many($rel)) {
|
||||||
if(!$this->query->isJoinedTo($component)) {
|
if(!$this->query->isJoinedTo($component)) {
|
||||||
$ancestry = $model->getClassAncestry();
|
$ancestry = $model->getClassAncestry();
|
||||||
$foreignKey = $model->getRemoteJoinField($rel);
|
$foreignKey = $model->getRemoteJoinField($rel);
|
||||||
$this->query->addLeftJoin($component,
|
$this->query->addLeftJoin($component,
|
||||||
"\"$component\".\"{$foreignKey}\" = \"{$ancestry[0]}\".\"ID\"");
|
"\"$component\".\"{$foreignKey}\" = \"{$ancestry[0]}\".\"ID\"");
|
||||||
/**
|
/**
|
||||||
* add join clause to the component's ancestry classes so that the search filter could search on
|
* add join clause to the component's ancestry classes so that the search filter could search on
|
||||||
* its ancestor fields.
|
* its ancestor fields.
|
||||||
*/
|
*/
|
||||||
$ancestry = ClassInfo::ancestry($component, true);
|
$ancestry = ClassInfo::ancestry($component, true);
|
||||||
if(!empty($ancestry)){
|
if(!empty($ancestry)){
|
||||||
$ancestry = array_reverse($ancestry);
|
$ancestry = array_reverse($ancestry);
|
||||||
foreach($ancestry as $ancestor){
|
foreach($ancestry as $ancestor){
|
||||||
if($ancestor != $component){
|
if($ancestor != $component){
|
||||||
$this->query->addInnerJoin($ancestor, "\"$component\".\"ID\" = \"$ancestor\".\"ID\"");
|
$this->query->addInnerJoin($ancestor, "\"$component\".\"ID\" = \"$ancestor\".\"ID\"");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$modelClass = $component;
|
$modelClass = $component;
|
||||||
|
|
||||||
} elseif ($component = $model->many_many($rel)) {
|
} elseif ($component = $model->many_many($rel)) {
|
||||||
list($parentClass, $componentClass, $parentField, $componentField, $relationTable) = $component;
|
list($parentClass, $componentClass, $parentField, $componentField, $relationTable) = $component;
|
||||||
$parentBaseClass = ClassInfo::baseDataClass($parentClass);
|
$parentBaseClass = ClassInfo::baseDataClass($parentClass);
|
||||||
$componentBaseClass = ClassInfo::baseDataClass($componentClass);
|
$componentBaseClass = ClassInfo::baseDataClass($componentClass);
|
||||||
$this->query->addInnerJoin($relationTable,
|
$this->query->addInnerJoin($relationTable,
|
||||||
"\"$relationTable\".\"$parentField\" = \"$parentBaseClass\".\"ID\"");
|
"\"$relationTable\".\"$parentField\" = \"$parentBaseClass\".\"ID\"");
|
||||||
$this->query->addLeftJoin($componentBaseClass,
|
$this->query->addLeftJoin($componentBaseClass,
|
||||||
"\"$relationTable\".\"$componentField\" = \"$componentBaseClass\".\"ID\"");
|
"\"$relationTable\".\"$componentField\" = \"$componentBaseClass\".\"ID\"");
|
||||||
if(ClassInfo::hasTable($componentClass)) {
|
if(ClassInfo::hasTable($componentClass)) {
|
||||||
$this->query->addLeftJoin($componentClass,
|
$this->query->addLeftJoin($componentClass,
|
||||||
"\"$relationTable\".\"$componentField\" = \"$componentClass\".\"ID\"");
|
"\"$relationTable\".\"$componentField\" = \"$componentClass\".\"ID\"");
|
||||||
}
|
}
|
||||||
$modelClass = $componentClass;
|
$modelClass = $componentClass;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $modelClass;
|
return $modelClass;
|
||||||
|
@ -422,7 +422,7 @@ abstract class SS_Database {
|
|||||||
//Indexes specified as arrays cannot be checked with this line: (it flattens out the array)
|
//Indexes specified as arrays cannot be checked with this line: (it flattens out the array)
|
||||||
if(!is_array($spec)) {
|
if(!is_array($spec)) {
|
||||||
$spec = preg_replace('/\s*,\s*/', ',', $spec);
|
$spec = preg_replace('/\s*,\s*/', ',', $spec);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!isset($this->tableList[strtolower($table)])) $newTable = true;
|
if(!isset($this->tableList[strtolower($table)])) $newTable = true;
|
||||||
|
|
||||||
@ -1142,7 +1142,7 @@ abstract class SS_Query implements Iterator {
|
|||||||
$result .= "<tr>";
|
$result .= "<tr>";
|
||||||
foreach($record as $k => $v) {
|
foreach($record as $k => $v) {
|
||||||
$result .= "<th>" . Convert::raw2xml($k) . "</th> ";
|
$result .= "<th>" . Convert::raw2xml($k) . "</th> ";
|
||||||
}
|
}
|
||||||
$result .= "</tr> \n";
|
$result .= "</tr> \n";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1219,7 +1219,7 @@ abstract class SS_Query implements Iterator {
|
|||||||
*/
|
*/
|
||||||
public function valid() {
|
public function valid() {
|
||||||
if(!$this->queryHasBegun) $this->next();
|
if(!$this->queryHasBegun) $this->next();
|
||||||
return $this->currentRecord !== false;
|
return $this->currentRecord !== false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -298,8 +298,8 @@ class DatabaseAdmin extends Controller {
|
|||||||
foreach($subclasses as $subclass) {
|
foreach($subclasses as $subclass) {
|
||||||
$id = $record['ID'];
|
$id = $record['ID'];
|
||||||
if(($record['ClassName'] != $subclass) &&
|
if(($record['ClassName'] != $subclass) &&
|
||||||
(!is_subclass_of($record['ClassName'], $subclass)) &&
|
(!is_subclass_of($record['ClassName'], $subclass)) &&
|
||||||
(isset($recordExists[$subclass][$id]))) {
|
(isset($recordExists[$subclass][$id]))) {
|
||||||
$sql = "DELETE FROM \"$subclass\" WHERE \"ID\" = $record[ID]";
|
$sql = "DELETE FROM \"$subclass\" WHERE \"ID\" = $record[ID]";
|
||||||
echo "<li>$sql";
|
echo "<li>$sql";
|
||||||
DB::query($sql);
|
DB::query($sql);
|
||||||
|
@ -66,10 +66,10 @@ class HasManyList extends RelationList {
|
|||||||
* @param $itemID The ID of the item to be removed
|
* @param $itemID The ID of the item to be removed
|
||||||
*/
|
*/
|
||||||
public function removeByID($itemID) {
|
public function removeByID($itemID) {
|
||||||
$item = $this->byID($itemID);
|
$item = $this->byID($itemID);
|
||||||
return $this->remove($item);
|
return $this->remove($item);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove an item from this relation.
|
* Remove an item from this relation.
|
||||||
* Doesn't actually remove the item, it just clears the foreign key value.
|
* Doesn't actually remove the item, it just clears the foreign key value.
|
||||||
@ -77,10 +77,10 @@ class HasManyList extends RelationList {
|
|||||||
* @todo Maybe we should delete the object instead?
|
* @todo Maybe we should delete the object instead?
|
||||||
*/
|
*/
|
||||||
public function remove($item) {
|
public function remove($item) {
|
||||||
if(!($item instanceof $this->dataClass)) {
|
if(!($item instanceof $this->dataClass)) {
|
||||||
throw new InvalidArgumentException("HasManyList::remove() expecting a $this->dataClass object, or ID",
|
throw new InvalidArgumentException("HasManyList::remove() expecting a $this->dataClass object, or ID",
|
||||||
E_USER_ERROR);
|
E_USER_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
$fk = $this->foreignKey;
|
$fk = $this->foreignKey;
|
||||||
$item->$fk = null;
|
$item->$fk = null;
|
||||||
|
@ -434,14 +434,14 @@ class Hierarchy extends DataExtension {
|
|||||||
public function Children() {
|
public function Children() {
|
||||||
if(!(isset($this->_cache_children) && $this->_cache_children)) {
|
if(!(isset($this->_cache_children) && $this->_cache_children)) {
|
||||||
$result = $this->owner->stageChildren(false);
|
$result = $this->owner->stageChildren(false);
|
||||||
if(isset($result)) {
|
if(isset($result)) {
|
||||||
$this->_cache_children = new ArrayList();
|
$this->_cache_children = new ArrayList();
|
||||||
foreach($result as $child) {
|
foreach($result as $child) {
|
||||||
if($child->canView()) {
|
if($child->canView()) {
|
||||||
$this->_cache_children->push($child);
|
$this->_cache_children->push($child);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return $this->_cache_children;
|
return $this->_cache_children;
|
||||||
}
|
}
|
||||||
@ -487,10 +487,10 @@ class Hierarchy extends DataExtension {
|
|||||||
// Next, go through the live children. Only some of these will be listed
|
// Next, go through the live children. Only some of these will be listed
|
||||||
$liveChildren = $this->owner->liveChildren(true, true);
|
$liveChildren = $this->owner->liveChildren(true, true);
|
||||||
if($liveChildren) {
|
if($liveChildren) {
|
||||||
$merged = new ArrayList();
|
$merged = new ArrayList();
|
||||||
$merged->merge($stageChildren);
|
$merged->merge($stageChildren);
|
||||||
$merged->merge($liveChildren);
|
$merged->merge($liveChildren);
|
||||||
$stageChildren = $merged;
|
$stageChildren = $merged;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -526,7 +526,7 @@ class Hierarchy extends DataExtension {
|
|||||||
throw new Exception('Hierarchy->AllHistoricalChildren() only works with Versioned extension applied');
|
throw new Exception('Hierarchy->AllHistoricalChildren() only works with Versioned extension applied');
|
||||||
}
|
}
|
||||||
|
|
||||||
return Versioned::get_including_deleted(ClassInfo::baseDataClass($this->owner->class),
|
return Versioned::get_including_deleted(ClassInfo::baseDataClass($this->owner->class),
|
||||||
"\"ParentID\" = " . (int)$this->owner->ID)->count();
|
"\"ParentID\" = " . (int)$this->owner->ID)->count();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,11 +117,11 @@ class ManyManyList extends RelationList {
|
|||||||
* @param $itemID The ID of the item to remove.
|
* @param $itemID The ID of the item to remove.
|
||||||
*/
|
*/
|
||||||
public function remove($item) {
|
public function remove($item) {
|
||||||
if(!($item instanceof $this->dataClass)) {
|
if(!($item instanceof $this->dataClass)) {
|
||||||
throw new InvalidArgumentException("ManyManyList::remove() expecting a $this->dataClass object");
|
throw new InvalidArgumentException("ManyManyList::remove() expecting a $this->dataClass object");
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->removeByID($item->ID);
|
return $this->removeByID($item->ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -130,7 +130,7 @@ class ManyManyList extends RelationList {
|
|||||||
* @param $itemID The item it
|
* @param $itemID The item it
|
||||||
*/
|
*/
|
||||||
public function removeByID($itemID) {
|
public function removeByID($itemID) {
|
||||||
if(!is_numeric($itemID)) throw new InvalidArgumentException("ManyManyList::removeById() expecting an ID");
|
if(!is_numeric($itemID)) throw new InvalidArgumentException("ManyManyList::removeById() expecting an ID");
|
||||||
|
|
||||||
$query = new SQLQuery("*", array("\"$this->joinTable\""));
|
$query = new SQLQuery("*", array("\"$this->joinTable\""));
|
||||||
$query->setDelete(true);
|
$query->setDelete(true);
|
||||||
@ -145,16 +145,16 @@ class ManyManyList extends RelationList {
|
|||||||
$query->execute();
|
$query->execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove all items from this many-many join. To remove a subset of items, filter it first.
|
* Remove all items from this many-many join. To remove a subset of items, filter it first.
|
||||||
*/
|
*/
|
||||||
public function removeAll() {
|
public function removeAll() {
|
||||||
$query = $this->dataQuery()->query();
|
$query = $this->dataQuery()->query();
|
||||||
$query->setDelete(true);
|
$query->setDelete(true);
|
||||||
$query->setSelect(array('*'));
|
$query->setSelect(array('*'));
|
||||||
$query->setFrom("\"$this->joinTable\"");
|
$query->setFrom("\"$this->joinTable\"");
|
||||||
$query->execute();
|
$query->execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find the extra field data for a single row of the relationship
|
* Find the extra field data for a single row of the relationship
|
||||||
|
@ -269,8 +269,8 @@ class MySQLDatabase extends SS_Database {
|
|||||||
if($alteredIndexes) foreach($alteredIndexes as $k => $v) {
|
if($alteredIndexes) foreach($alteredIndexes as $k => $v) {
|
||||||
$alterList[] .= "DROP INDEX \"$k\"";
|
$alterList[] .= "DROP INDEX \"$k\"";
|
||||||
$alterList[] .= "ADD ". $this->getIndexSqlDefinition($k, $v);
|
$alterList[] .= "ADD ". $this->getIndexSqlDefinition($k, $v);
|
||||||
}
|
}
|
||||||
|
|
||||||
if($alteredOptions && isset($alteredOptions[get_class($this)])) {
|
if($alteredOptions && isset($alteredOptions[get_class($this)])) {
|
||||||
if(!isset($this->indexList[$tableName])) {
|
if(!isset($this->indexList[$tableName])) {
|
||||||
$this->indexList[$tableName] = $this->indexList($tableName);
|
$this->indexList[$tableName] = $this->indexList($tableName);
|
||||||
@ -298,7 +298,7 @@ class MySQLDatabase extends SS_Database {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$alterations = implode(",\n", $alterList);
|
$alterations = implode(",\n", $alterList);
|
||||||
$this->query("ALTER TABLE \"$tableName\" $alterations");
|
$this->query("ALTER TABLE \"$tableName\" $alterations");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -467,9 +467,9 @@ class MySQLDatabase extends SS_Database {
|
|||||||
|
|
||||||
$indexSpec = trim($indexSpec);
|
$indexSpec = trim($indexSpec);
|
||||||
if($indexSpec[0] != '(') list($indexType, $indexFields) = explode(' ',$indexSpec,2);
|
if($indexSpec[0] != '(') list($indexType, $indexFields) = explode(' ',$indexSpec,2);
|
||||||
else $indexFields = $indexSpec;
|
else $indexFields = $indexSpec;
|
||||||
|
|
||||||
if(!isset($indexType))
|
if(!isset($indexType))
|
||||||
$indexType = "index";
|
$indexType = "index";
|
||||||
|
|
||||||
if($indexType=='using')
|
if($indexType=='using')
|
||||||
@ -499,15 +499,15 @@ class MySQLDatabase extends SS_Database {
|
|||||||
$indexSpec=$this->convertIndexSpec($indexSpec);
|
$indexSpec=$this->convertIndexSpec($indexSpec);
|
||||||
|
|
||||||
$indexSpec = trim($indexSpec);
|
$indexSpec = trim($indexSpec);
|
||||||
if($indexSpec[0] != '(') {
|
if($indexSpec[0] != '(') {
|
||||||
list($indexType, $indexFields) = explode(' ',$indexSpec,2);
|
list($indexType, $indexFields) = explode(' ',$indexSpec,2);
|
||||||
} else {
|
} else {
|
||||||
$indexFields = $indexSpec;
|
$indexFields = $indexSpec;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!$indexType) {
|
if(!$indexType) {
|
||||||
$indexType = "index";
|
$indexType = "index";
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->query("ALTER TABLE \"$tableName\" DROP INDEX \"$indexName\"");
|
$this->query("ALTER TABLE \"$tableName\" DROP INDEX \"$indexName\"");
|
||||||
$this->query("ALTER TABLE \"$tableName\" ADD $indexType \"$indexName\" $indexFields");
|
$this->query("ALTER TABLE \"$tableName\" ADD $indexType \"$indexName\" $indexFields");
|
||||||
@ -822,19 +822,19 @@ class MySQLDatabase extends SS_Database {
|
|||||||
if(!class_exists('File')) throw new Exception('MySQLDatabase->searchEngine() requires "File" class');
|
if(!class_exists('File')) throw new Exception('MySQLDatabase->searchEngine() requires "File" class');
|
||||||
|
|
||||||
$fileFilter = '';
|
$fileFilter = '';
|
||||||
$keywords = Convert::raw2sql($keywords);
|
$keywords = Convert::raw2sql($keywords);
|
||||||
$htmlEntityKeywords = htmlentities($keywords, ENT_NOQUOTES, 'UTF-8');
|
$htmlEntityKeywords = htmlentities($keywords, ENT_NOQUOTES, 'UTF-8');
|
||||||
|
|
||||||
$extraFilters = array('SiteTree' => '', 'File' => '');
|
$extraFilters = array('SiteTree' => '', 'File' => '');
|
||||||
|
|
||||||
if($booleanSearch) $boolean = "IN BOOLEAN MODE";
|
if($booleanSearch) $boolean = "IN BOOLEAN MODE";
|
||||||
|
|
||||||
if($extraFilter) {
|
if($extraFilter) {
|
||||||
$extraFilters['SiteTree'] = " AND $extraFilter";
|
$extraFilters['SiteTree'] = " AND $extraFilter";
|
||||||
|
|
||||||
if($alternativeFileFilter) $extraFilters['File'] = " AND $alternativeFileFilter";
|
if($alternativeFileFilter) $extraFilters['File'] = " AND $alternativeFileFilter";
|
||||||
else $extraFilters['File'] = $extraFilters['SiteTree'];
|
else $extraFilters['File'] = $extraFilters['SiteTree'];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always ensure that only pages with ShowInSearch = 1 can be searched
|
// Always ensure that only pages with ShowInSearch = 1 can be searched
|
||||||
$extraFilters['SiteTree'] .= " AND ShowInSearch <> 0";
|
$extraFilters['SiteTree'] .= " AND ShowInSearch <> 0";
|
||||||
@ -982,7 +982,7 @@ class MySQLDatabase extends SS_Database {
|
|||||||
$boolean = $booleanSearch ? "IN BOOLEAN MODE" : "";
|
$boolean = $booleanSearch ? "IN BOOLEAN MODE" : "";
|
||||||
$fieldNames = '"' . implode('", "', $fields) . '"';
|
$fieldNames = '"' . implode('", "', $fields) . '"';
|
||||||
|
|
||||||
$SQL_keywords = Convert::raw2sql($keywords);
|
$SQL_keywords = Convert::raw2sql($keywords);
|
||||||
$SQL_htmlEntityKeywords = Convert::raw2sql(htmlentities($keywords, ENT_NOQUOTES, 'UTF-8'));
|
$SQL_htmlEntityKeywords = Convert::raw2sql(htmlentities($keywords, ENT_NOQUOTES, 'UTF-8'));
|
||||||
|
|
||||||
return "(MATCH ($fieldNames) AGAINST ('$SQL_keywords' $boolean) + MATCH ($fieldNames)"
|
return "(MATCH ($fieldNames) AGAINST ('$SQL_keywords' $boolean) + MATCH ($fieldNames)"
|
||||||
@ -1151,7 +1151,7 @@ class MySQLDatabase extends SS_Database {
|
|||||||
* @param string $date2 to be substracted of $date1, can be either 'now', literal datetime like
|
* @param string $date2 to be substracted of $date1, can be either 'now', literal datetime like
|
||||||
* '1973-10-14 10:30:00' or field name, e.g. '"SiteTree"."Created"'
|
* '1973-10-14 10:30:00' or field name, e.g. '"SiteTree"."Created"'
|
||||||
* @return string SQL datetime expression to query for the interval between $date1 and $date2 in seconds which
|
* @return string SQL datetime expression to query for the interval between $date1 and $date2 in seconds which
|
||||||
* is the result of the substraction
|
* is the result of the substraction
|
||||||
*/
|
*/
|
||||||
public function datetimeDifferenceClause($date1, $date2) {
|
public function datetimeDifferenceClause($date1, $date2) {
|
||||||
|
|
||||||
|
@ -908,11 +908,11 @@ class SQLQuery {
|
|||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function __toString() {
|
public function __toString() {
|
||||||
try {
|
try {
|
||||||
return $this->sql();
|
return $this->sql();
|
||||||
} catch(Exception $e) {
|
} catch(Exception $e) {
|
||||||
return "<sql query>";
|
return "<sql query>";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -53,6 +53,6 @@ class SS_Transliterator extends Object {
|
|||||||
* Transliteration using iconv()
|
* Transliteration using iconv()
|
||||||
*/
|
*/
|
||||||
protected function useIconv($source) {
|
protected function useIconv($source) {
|
||||||
return iconv("utf-8", "us-ascii//IGNORE//TRANSLIT", $source);
|
return iconv("utf-8", "us-ascii//IGNORE//TRANSLIT", $source);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -136,8 +136,8 @@ class Versioned extends DataExtension {
|
|||||||
* @todo Should this all go into VersionedDataQuery?
|
* @todo Should this all go into VersionedDataQuery?
|
||||||
*/
|
*/
|
||||||
public function augmentSQL(SQLQuery &$query, DataQuery &$dataQuery = null) {
|
public function augmentSQL(SQLQuery &$query, DataQuery &$dataQuery = null) {
|
||||||
$baseTable = ClassInfo::baseDataClass($dataQuery->dataClass());
|
$baseTable = ClassInfo::baseDataClass($dataQuery->dataClass());
|
||||||
|
|
||||||
switch($dataQuery->getQueryParam('Versioned.mode')) {
|
switch($dataQuery->getQueryParam('Versioned.mode')) {
|
||||||
// Noop
|
// Noop
|
||||||
case '':
|
case '':
|
||||||
@ -266,8 +266,8 @@ class Versioned extends DataExtension {
|
|||||||
*/
|
*/
|
||||||
function augmentLoadLazyFields(SQLQuery &$query, DataQuery &$dataQuery = null, $record) {
|
function augmentLoadLazyFields(SQLQuery &$query, DataQuery &$dataQuery = null, $record) {
|
||||||
$dataClass = $dataQuery->dataClass();
|
$dataClass = $dataQuery->dataClass();
|
||||||
if (isset($record['Version'])){
|
if (isset($record['Version'])){
|
||||||
$dataQuery->where("\"$dataClass\".\"RecordID\" = " . $record['ID']);
|
$dataQuery->where("\"$dataClass\".\"RecordID\" = " . $record['ID']);
|
||||||
$dataQuery->where("\"$dataClass\".\"Version\" = " . $record['Version']);
|
$dataQuery->where("\"$dataClass\".\"Version\" = " . $record['Version']);
|
||||||
$dataQuery->setQueryParam('Versioned.mode', 'all_versions');
|
$dataQuery->setQueryParam('Versioned.mode', 'all_versions');
|
||||||
}
|
}
|
||||||
|
@ -330,23 +330,23 @@ class Date extends DBField {
|
|||||||
|
|
||||||
|
|
||||||
public function days_between($fyear, $fmonth, $fday, $tyear, $tmonth, $tday){
|
public function days_between($fyear, $fmonth, $fday, $tyear, $tmonth, $tday){
|
||||||
return abs((mktime ( 0, 0, 0, $fmonth, $fday, $fyear) - mktime ( 0, 0, 0, $tmonth, $tday, $tyear))/(60*60*24));
|
return abs((mktime ( 0, 0, 0, $fmonth, $fday, $fyear) - mktime ( 0, 0, 0, $tmonth, $tday, $tyear))/(60*60*24));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function day_before($fyear, $fmonth, $fday){
|
public function day_before($fyear, $fmonth, $fday){
|
||||||
return date ("Y-m-d", mktime (0,0,0,$fmonth,$fday-1,$fyear));
|
return date ("Y-m-d", mktime (0,0,0,$fmonth,$fday-1,$fyear));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function next_day($fyear, $fmonth, $fday){
|
public function next_day($fyear, $fmonth, $fday){
|
||||||
return date ("Y-m-d", mktime (0,0,0,$fmonth,$fday+1,$fyear));
|
return date ("Y-m-d", mktime (0,0,0,$fmonth,$fday+1,$fyear));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function weekday($fyear, $fmonth, $fday){ // 0 is a Monday
|
public function weekday($fyear, $fmonth, $fday){ // 0 is a Monday
|
||||||
return (((mktime ( 0, 0, 0, $fmonth, $fday, $fyear) - mktime ( 0, 0, 0, 7, 17, 2006))/(60*60*24))+700000) % 7;
|
return (((mktime ( 0, 0, 0, $fmonth, $fday, $fyear) - mktime ( 0, 0, 0, 7, 17, 2006))/(60*60*24))+700000) % 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function prior_monday($fyear, $fmonth, $fday){
|
public function prior_monday($fyear, $fmonth, $fday){
|
||||||
return date ("Y-m-d", mktime (0,0,0,$fmonth,$fday-$this->weekday($fyear, $fmonth, $fday),$fyear));
|
return date ("Y-m-d", mktime (0,0,0,$fmonth,$fday-$this->weekday($fyear, $fmonth, $fday),$fyear));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -33,7 +33,7 @@ class Text extends StringField {
|
|||||||
'NoHTML' => 'Text',
|
'NoHTML' => 'Text',
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* (non-PHPdoc)
|
* (non-PHPdoc)
|
||||||
* @see DBField::requireField()
|
* @see DBField::requireField()
|
||||||
*/
|
*/
|
||||||
|
@ -17,8 +17,8 @@ class Varchar extends StringField {
|
|||||||
);
|
);
|
||||||
|
|
||||||
protected $size;
|
protected $size;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a new short text field
|
* Construct a new short text field
|
||||||
*
|
*
|
||||||
* @param $name string The name of the field
|
* @param $name string The name of the field
|
||||||
@ -27,12 +27,12 @@ class Varchar extends StringField {
|
|||||||
* See {@link StringField::setOptions()} for information on the available options
|
* See {@link StringField::setOptions()} for information on the available options
|
||||||
* @return unknown_type
|
* @return unknown_type
|
||||||
*/
|
*/
|
||||||
public function __construct($name = null, $size = 50, $options = array()) {
|
public function __construct($name = null, $size = 50, $options = array()) {
|
||||||
$this->size = $size ? $size : 50;
|
$this->size = $size ? $size : 50;
|
||||||
parent::__construct($name, $options);
|
parent::__construct($name, $options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* (non-PHPdoc)
|
* (non-PHPdoc)
|
||||||
* @see DBField::requireField()
|
* @see DBField::requireField()
|
||||||
*/
|
*/
|
||||||
|
@ -27,7 +27,7 @@ class BBCodeParser extends TextParser {
|
|||||||
* @var Boolean
|
* @var Boolean
|
||||||
*/
|
*/
|
||||||
protected static $allowSimilies = false;
|
protected static $allowSimilies = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the location of the smiles folder. By default use the ones in framework
|
* Set the location of the smiles folder. By default use the ones in framework
|
||||||
* but this can be overridden by setting BBCodeParser::set_icon_folder('themes/yourtheme/images/');
|
* but this can be overridden by setting BBCodeParser::set_icon_folder('themes/yourtheme/images/');
|
||||||
@ -165,7 +165,7 @@ class BBCodeParser extends TextParser {
|
|||||||
'#(?<!\w):\((?!\w)#i' => " <img src='".BBCodeParser::smilies_location(). "/sad.gif'> ", // :(
|
'#(?<!\w):\((?!\w)#i' => " <img src='".BBCodeParser::smilies_location(). "/sad.gif'> ", // :(
|
||||||
'#(?<!\w):-\((?!\w)#i' => " <img src='".BBCodeParser::smilies_location(). "/sad.gif'> ", // :-(
|
'#(?<!\w):-\((?!\w)#i' => " <img src='".BBCodeParser::smilies_location(). "/sad.gif'> ", // :-(
|
||||||
'#(?<!\w):p(?!\w)#i' => " <img src='".BBCodeParser::smilies_location(). "/tongue.gif'> ", // :p
|
'#(?<!\w):p(?!\w)#i' => " <img src='".BBCodeParser::smilies_location(). "/tongue.gif'> ", // :p
|
||||||
'#(?<!\w)8-\)(?!\w)#i' => " <img src='".BBCodeParser::smilies_location(). "/cool.gif'> ", // 8-)
|
'#(?<!\w)8-\)(?!\w)#i' => " <img src='".BBCodeParser::smilies_location(). "/cool.gif'> ", // 8-)
|
||||||
'#(?<!\w):\^\)(?!\w)#i' => " <img src='".BBCodeParser::smilies_location(). "/confused.gif'> " // :^)
|
'#(?<!\w):\^\)(?!\w)#i' => " <img src='".BBCodeParser::smilies_location(). "/confused.gif'> " // :^)
|
||||||
);
|
);
|
||||||
$this->content = preg_replace(array_keys($smilies), array_values($smilies), $this->content);
|
$this->content = preg_replace(array_keys($smilies), array_values($smilies), $this->content);
|
||||||
|
@ -55,819 +55,818 @@
|
|||||||
*/
|
*/
|
||||||
class SSHTMLBBCodeParser
|
class SSHTMLBBCodeParser
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* An array of tags parsed by the engine, should be overwritten by filters
|
* An array of tags parsed by the engine, should be overwritten by filters
|
||||||
*
|
*
|
||||||
* @access private
|
* @access private
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
var $_definedTags = array();
|
var $_definedTags = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A string containing the input
|
* A string containing the input
|
||||||
*
|
*
|
||||||
* @access private
|
* @access private
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
var $_text = '';
|
var $_text = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A string containing the preparsed input
|
* A string containing the preparsed input
|
||||||
*
|
*
|
||||||
* @access private
|
* @access private
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
var $_preparsed = '';
|
var $_preparsed = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An array tags and texts build from the input text
|
* An array tags and texts build from the input text
|
||||||
*
|
*
|
||||||
* @access private
|
* @access private
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
var $_tagArray = array();
|
var $_tagArray = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A string containing the parsed version of the text
|
* A string containing the parsed version of the text
|
||||||
*
|
*
|
||||||
* @access private
|
* @access private
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
var $_parsed = '';
|
var $_parsed = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An array of options, filled by an ini file or through the contructor
|
* An array of options, filled by an ini file or through the contructor
|
||||||
*
|
*
|
||||||
* @access private
|
* @access private
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
var $_options = array(
|
var $_options = array(
|
||||||
'quotestyle' => 'double',
|
'quotestyle' => 'double',
|
||||||
'quotewhat' => 'all',
|
'quotewhat' => 'all',
|
||||||
'open' => '[',
|
'open' => '[',
|
||||||
'close' => ']',
|
'close' => ']',
|
||||||
'xmlclose' => true,
|
'xmlclose' => true,
|
||||||
'filters' => 'Basic'
|
'filters' => 'Basic'
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An array of filters used for parsing
|
* An array of filters used for parsing
|
||||||
*
|
*
|
||||||
* @access private
|
* @access private
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
var $_filters = array();
|
var $_filters = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor, initialises the options and filters
|
* Constructor, initialises the options and filters
|
||||||
*
|
*
|
||||||
* Sets the private variable _options with base options defined with
|
* Sets the private variable _options with base options defined with
|
||||||
* &PEAR::getStaticProperty(), overwriting them with (if present)
|
* &PEAR::getStaticProperty(), overwriting them with (if present)
|
||||||
* the argument to this method.
|
* the argument to this method.
|
||||||
* Then it sets the extra options to properly escape the tag
|
* Then it sets the extra options to properly escape the tag
|
||||||
* characters in preg_replace() etc. The set options are
|
* characters in preg_replace() etc. The set options are
|
||||||
* then stored back with &PEAR::getStaticProperty(), so that the filter
|
* then stored back with &PEAR::getStaticProperty(), so that the filter
|
||||||
* classes can use them.
|
* classes can use them.
|
||||||
* All the filters in the options are initialised and their defined tags
|
* All the filters in the options are initialised and their defined tags
|
||||||
* are copied into the private variable _definedTags.
|
* are copied into the private variable _definedTags.
|
||||||
*
|
*
|
||||||
* @param array options to use, can be left out
|
* @param array options to use, can be left out
|
||||||
* @return none
|
* @return none
|
||||||
* @access public
|
* @access public
|
||||||
* @author Stijn de Reede <sjr@gmx.co.uk>
|
* @author Stijn de Reede <sjr@gmx.co.uk>
|
||||||
*/
|
*/
|
||||||
public function SSHTMLBBCodeParser($options = array())
|
public function SSHTMLBBCodeParser($options = array())
|
||||||
{
|
{
|
||||||
// set the already set options
|
// set the already set options
|
||||||
$baseoptions = &SSHTMLBBCodeParser::getStaticProperty('SSHTMLBBCodeParser', '_options');
|
$baseoptions = &SSHTMLBBCodeParser::getStaticProperty('SSHTMLBBCodeParser', '_options');
|
||||||
if (is_array($baseoptions)) {
|
if (is_array($baseoptions)) {
|
||||||
foreach ($baseoptions as $k => $v) {
|
foreach ($baseoptions as $k => $v) {
|
||||||
$this->_options[$k] = $v;
|
$this->_options[$k] = $v;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// set the options passed as an argument
|
// set the options passed as an argument
|
||||||
foreach ($options as $k => $v ) {
|
foreach ($options as $k => $v ) {
|
||||||
$this->_options[$k] = $v;
|
$this->_options[$k] = $v;
|
||||||
}
|
}
|
||||||
|
|
||||||
// add escape open and close chars to the options for preg escaping
|
// add escape open and close chars to the options for preg escaping
|
||||||
$preg_escape = '\^$.[]|()?*+{}';
|
$preg_escape = '\^$.[]|()?*+{}';
|
||||||
if ($this->_options['open'] != '' && strpos($preg_escape, $this->_options['open'])) {
|
if ($this->_options['open'] != '' && strpos($preg_escape, $this->_options['open'])) {
|
||||||
$this->_options['open_esc'] = "\\".$this->_options['open'];
|
$this->_options['open_esc'] = "\\".$this->_options['open'];
|
||||||
} else {
|
} else {
|
||||||
$this->_options['open_esc'] = $this->_options['open'];
|
$this->_options['open_esc'] = $this->_options['open'];
|
||||||
}
|
}
|
||||||
if ($this->_options['close'] != '' && strpos($preg_escape, $this->_options['close'])) {
|
if ($this->_options['close'] != '' && strpos($preg_escape, $this->_options['close'])) {
|
||||||
$this->_options['close_esc'] = "\\".$this->_options['close'];
|
$this->_options['close_esc'] = "\\".$this->_options['close'];
|
||||||
} else {
|
} else {
|
||||||
$this->_options['close_esc'] = $this->_options['close'];
|
$this->_options['close_esc'] = $this->_options['close'];
|
||||||
}
|
}
|
||||||
|
|
||||||
// set the options back so that child classes can use them */
|
// set the options back so that child classes can use them */
|
||||||
$baseoptions = $this->_options;
|
$baseoptions = $this->_options;
|
||||||
unset($baseoptions);
|
unset($baseoptions);
|
||||||
|
|
||||||
// return if this is a subclass
|
// return if this is a subclass
|
||||||
if (is_subclass_of($this, 'SSHTMLBBCodeParser_Filter')) {
|
if (is_subclass_of($this, 'SSHTMLBBCodeParser_Filter')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// extract the definedTags from subclasses */
|
// extract the definedTags from subclasses */
|
||||||
$this->addFilters($this->_options['filters']);
|
$this->addFilters($this->_options['filters']);
|
||||||
}
|
}
|
||||||
|
|
||||||
static function &getStaticProperty($class, $var)
|
static function &getStaticProperty($class, $var)
|
||||||
{
|
{
|
||||||
static $properties;
|
static $properties;
|
||||||
if (!isset($properties[$class])) {
|
if (!isset($properties[$class])) {
|
||||||
$properties[$class] = array();
|
$properties[$class] = array();
|
||||||
}
|
}
|
||||||
if (!array_key_exists($var, $properties[$class])) {
|
if (!array_key_exists($var, $properties[$class])) {
|
||||||
$properties[$class][$var] = null;
|
$properties[$class][$var] = null;
|
||||||
}
|
}
|
||||||
return $properties[$class][$var];
|
return $properties[$class][$var];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Option setter
|
* Option setter
|
||||||
*
|
*
|
||||||
* @param string option name
|
* @param string option name
|
||||||
* @param mixed option value
|
* @param mixed option value
|
||||||
* @author Lorenzo Alberton <l.alberton@quipo.it>
|
* @author Lorenzo Alberton <l.alberton@quipo.it>
|
||||||
*/
|
*/
|
||||||
public function setOption($name, $value)
|
public function setOption($name, $value)
|
||||||
{
|
{
|
||||||
$this->_options[$name] = $value;
|
$this->_options[$name] = $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a new filter
|
* Add a new filter
|
||||||
*
|
*
|
||||||
* @param string filter
|
* @param string filter
|
||||||
* @author Lorenzo Alberton <l.alberton@quipo.it>
|
* @author Lorenzo Alberton <l.alberton@quipo.it>
|
||||||
*/
|
*/
|
||||||
public function addFilter($filter)
|
public function addFilter($filter)
|
||||||
{
|
{
|
||||||
|
$filter = ucfirst($filter);
|
||||||
$filter = ucfirst($filter);
|
if (!array_key_exists($filter, $this->_filters)) {
|
||||||
if (!array_key_exists($filter, $this->_filters)) {
|
$class = 'SSHTMLBBCodeParser_Filter_'.$filter;
|
||||||
$class = 'SSHTMLBBCodeParser_Filter_'.$filter;
|
if (fopen('BBCodeParser/Filter/'.$filter.'.php','r',true)) {
|
||||||
if (fopen('BBCodeParser/Filter/'.$filter.'.php','r',true)) {
|
include_once 'BBCodeParser/Filter/'.$filter.'.php';
|
||||||
include_once 'BBCodeParser/Filter/'.$filter.'.php';
|
}
|
||||||
}
|
if (!class_exists($class)) {
|
||||||
if (!class_exists($class)) {
|
|
||||||
|
|
||||||
//PEAR::raiseError("Failed to load filter $filter", null, PEAR_ERROR_DIE);
|
//PEAR::raiseError("Failed to load filter $filter", null, PEAR_ERROR_DIE);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$this->_filters[$filter] = new $class;
|
$this->_filters[$filter] = new $class;
|
||||||
$this->_definedTags = array_merge(
|
$this->_definedTags = array_merge(
|
||||||
$this->_definedTags,
|
$this->_definedTags,
|
||||||
$this->_filters[$filter]->_definedTags
|
$this->_filters[$filter]->_definedTags
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove an existing filter
|
* Remove an existing filter
|
||||||
*
|
*
|
||||||
* @param string $filter
|
* @param string $filter
|
||||||
* @author Lorenzo Alberton <l.alberton@quipo.it>
|
* @author Lorenzo Alberton <l.alberton@quipo.it>
|
||||||
*/
|
*/
|
||||||
public function removeFilter($filter)
|
public function removeFilter($filter)
|
||||||
{
|
{
|
||||||
$filter = ucfirst(trim($filter));
|
$filter = ucfirst(trim($filter));
|
||||||
if (!empty($filter) && array_key_exists($filter, $this->_filters)) {
|
if (!empty($filter) && array_key_exists($filter, $this->_filters)) {
|
||||||
unset($this->_filters[$filter]);
|
unset($this->_filters[$filter]);
|
||||||
}
|
}
|
||||||
// also remove the related $this->_definedTags for this filter,
|
// also remove the related $this->_definedTags for this filter,
|
||||||
// preserving the others
|
// preserving the others
|
||||||
$this->_definedTags = array();
|
$this->_definedTags = array();
|
||||||
foreach (array_keys($this->_filters) as $filter) {
|
foreach (array_keys($this->_filters) as $filter) {
|
||||||
$this->_definedTags = array_merge(
|
$this->_definedTags = array_merge(
|
||||||
$this->_definedTags,
|
$this->_definedTags,
|
||||||
$this->_filters[$filter]->_definedTags
|
$this->_filters[$filter]->_definedTags
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add new filters
|
* Add new filters
|
||||||
*
|
*
|
||||||
* @param mixed (array or string)
|
* @param mixed (array or string)
|
||||||
* @return boolean true if all ok, false if not.
|
* @return boolean true if all ok, false if not.
|
||||||
* @author Lorenzo Alberton <l.alberton@quipo.it>
|
* @author Lorenzo Alberton <l.alberton@quipo.it>
|
||||||
*/
|
*/
|
||||||
public function addFilters($filters)
|
public function addFilters($filters)
|
||||||
{
|
{
|
||||||
if (is_string($filters)) {
|
if (is_string($filters)) {
|
||||||
//comma-separated list
|
//comma-separated list
|
||||||
if (strpos($filters, ',') !== false) {
|
if (strpos($filters, ',') !== false) {
|
||||||
$filters = explode(',', $filters);
|
$filters = explode(',', $filters);
|
||||||
} else {
|
} else {
|
||||||
$filters = array($filters);
|
$filters = array($filters);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!is_array($filters)) {
|
if (!is_array($filters)) {
|
||||||
//invalid format
|
//invalid format
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
foreach ($filters as $filter) {
|
foreach ($filters as $filter) {
|
||||||
if (trim($filter)){
|
if (trim($filter)){
|
||||||
$this->addFilter($filter);
|
$this->addFilter($filter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Executes statements before the actual array building starts
|
* Executes statements before the actual array building starts
|
||||||
*
|
*
|
||||||
* This method should be overwritten in a filter if you want to do
|
* This method should be overwritten in a filter if you want to do
|
||||||
* something before the parsing process starts. This can be useful to
|
* something before the parsing process starts. This can be useful to
|
||||||
* allow certain short alternative tags which then can be converted into
|
* allow certain short alternative tags which then can be converted into
|
||||||
* proper tags with preg_replace() calls.
|
* proper tags with preg_replace() calls.
|
||||||
* The main class walks through all the filters and and calls this
|
* The main class walks through all the filters and and calls this
|
||||||
* method. The filters should modify their private $_preparsed
|
* method. The filters should modify their private $_preparsed
|
||||||
* variable, with input from $_text.
|
* variable, with input from $_text.
|
||||||
*
|
*
|
||||||
* @return none
|
* @return none
|
||||||
* @access private
|
* @access private
|
||||||
* @see $_text
|
* @see $_text
|
||||||
* @author Stijn de Reede <sjr@gmx.co.uk>
|
* @author Stijn de Reede <sjr@gmx.co.uk>
|
||||||
*/
|
*/
|
||||||
public function _preparse()
|
public function _preparse()
|
||||||
{
|
{
|
||||||
// default: assign _text to _preparsed, to be overwritten by filters
|
// default: assign _text to _preparsed, to be overwritten by filters
|
||||||
$this->_preparsed = $this->_text;
|
$this->_preparsed = $this->_text;
|
||||||
|
|
||||||
// return if this is a subclass
|
// return if this is a subclass
|
||||||
if (is_subclass_of($this, 'SSHTMLBBCodeParser')) {
|
if (is_subclass_of($this, 'SSHTMLBBCodeParser')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// walk through the filters and execute _preparse
|
// walk through the filters and execute _preparse
|
||||||
foreach ($this->_filters as $filter) {
|
foreach ($this->_filters as $filter) {
|
||||||
$filter->setText($this->_preparsed);
|
$filter->setText($this->_preparsed);
|
||||||
$filter->_preparse();
|
$filter->_preparse();
|
||||||
$this->_preparsed = $filter->getPreparsed();
|
$this->_preparsed = $filter->getPreparsed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds the tag array from the input string $_text
|
* Builds the tag array from the input string $_text
|
||||||
*
|
*
|
||||||
* An array consisting of tag and text elements is contructed from the
|
* An array consisting of tag and text elements is contructed from the
|
||||||
* $_preparsed variable. The method uses _buildTag() to check if a tag is
|
* $_preparsed variable. The method uses _buildTag() to check if a tag is
|
||||||
* valid and to build the actual tag to be added to the tag array.
|
* valid and to build the actual tag to be added to the tag array.
|
||||||
*
|
*
|
||||||
* @todo - rewrite whole method, as this one is old and probably slow
|
* @todo - rewrite whole method, as this one is old and probably slow
|
||||||
* - see if a recursive method would be better than an iterative one
|
* - see if a recursive method would be better than an iterative one
|
||||||
*
|
*
|
||||||
* @return none
|
* @return none
|
||||||
* @access private
|
* @access private
|
||||||
* @see _buildTag()
|
* @see _buildTag()
|
||||||
* @see $_text
|
* @see $_text
|
||||||
* @see $_tagArray
|
* @see $_tagArray
|
||||||
* @author Stijn de Reede <sjr@gmx.co.uk>
|
* @author Stijn de Reede <sjr@gmx.co.uk>
|
||||||
*/
|
*/
|
||||||
public function _buildTagArray()
|
public function _buildTagArray()
|
||||||
{
|
{
|
||||||
$this->_tagArray = array();
|
$this->_tagArray = array();
|
||||||
$str = $this->_preparsed;
|
$str = $this->_preparsed;
|
||||||
$strPos = 0;
|
$strPos = 0;
|
||||||
$strLength = strlen($str);
|
$strLength = strlen($str);
|
||||||
|
|
||||||
while (($strPos < $strLength)) {
|
while (($strPos < $strLength)) {
|
||||||
$tag = array();
|
$tag = array();
|
||||||
$openPos = strpos($str, $this->_options['open'], $strPos);
|
$openPos = strpos($str, $this->_options['open'], $strPos);
|
||||||
if ($openPos === false) {
|
if ($openPos === false) {
|
||||||
$openPos = $strLength;
|
$openPos = $strLength;
|
||||||
$nextOpenPos = $strLength;
|
$nextOpenPos = $strLength;
|
||||||
}
|
}
|
||||||
if ($openPos + 1 > $strLength) {
|
if ($openPos + 1 > $strLength) {
|
||||||
$nextOpenPos = $strLength;
|
$nextOpenPos = $strLength;
|
||||||
} else {
|
} else {
|
||||||
$nextOpenPos = strpos($str, $this->_options['open'], $openPos + 1);
|
$nextOpenPos = strpos($str, $this->_options['open'], $openPos + 1);
|
||||||
if ($nextOpenPos === false) {
|
if ($nextOpenPos === false) {
|
||||||
$nextOpenPos = $strLength;
|
$nextOpenPos = $strLength;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$closePos = strpos($str, $this->_options['close'], $strPos);
|
$closePos = strpos($str, $this->_options['close'], $strPos);
|
||||||
if ($closePos === false) {
|
if ($closePos === false) {
|
||||||
$closePos = $strLength + 1;
|
$closePos = $strLength + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($openPos == $strPos) {
|
if ($openPos == $strPos) {
|
||||||
if (($nextOpenPos < $closePos)) {
|
if (($nextOpenPos < $closePos)) {
|
||||||
// new open tag before closing tag: treat as text
|
// new open tag before closing tag: treat as text
|
||||||
$newPos = $nextOpenPos;
|
$newPos = $nextOpenPos;
|
||||||
$tag['text'] = substr($str, $strPos, $nextOpenPos - $strPos);
|
$tag['text'] = substr($str, $strPos, $nextOpenPos - $strPos);
|
||||||
$tag['type'] = 0;
|
$tag['type'] = 0;
|
||||||
} else {
|
} else {
|
||||||
// possible valid tag
|
// possible valid tag
|
||||||
$newPos = $closePos + 1;
|
$newPos = $closePos + 1;
|
||||||
$newTag = $this->_buildTag(substr($str, $strPos, $closePos - $strPos + 1));
|
$newTag = $this->_buildTag(substr($str, $strPos, $closePos - $strPos + 1));
|
||||||
if (($newTag !== false)) {
|
if (($newTag !== false)) {
|
||||||
$tag = $newTag;
|
$tag = $newTag;
|
||||||
} else {
|
} else {
|
||||||
// no valid tag after all
|
// no valid tag after all
|
||||||
$tag['text'] = substr($str, $strPos, $closePos - $strPos + 1);
|
$tag['text'] = substr($str, $strPos, $closePos - $strPos + 1);
|
||||||
$tag['type'] = 0;
|
$tag['type'] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// just text
|
// just text
|
||||||
$newPos = $openPos;
|
$newPos = $openPos;
|
||||||
$tag['text'] = substr($str, $strPos, $openPos - $strPos);
|
$tag['text'] = substr($str, $strPos, $openPos - $strPos);
|
||||||
$tag['type'] = 0;
|
$tag['type'] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// join 2 following text elements
|
// join 2 following text elements
|
||||||
if ($tag['type'] === 0 && isset($prev) && $prev['type'] === 0) {
|
if ($tag['type'] === 0 && isset($prev) && $prev['type'] === 0) {
|
||||||
$tag['text'] = $prev['text'].$tag['text'];
|
$tag['text'] = $prev['text'].$tag['text'];
|
||||||
array_pop($this->_tagArray);
|
array_pop($this->_tagArray);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->_tagArray[] = $tag;
|
$this->_tagArray[] = $tag;
|
||||||
$prev = $tag;
|
$prev = $tag;
|
||||||
$strPos = $newPos;
|
$strPos = $newPos;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds a tag from the input string
|
* Builds a tag from the input string
|
||||||
*
|
*
|
||||||
* This method builds a tag array based on the string it got as an
|
* This method builds a tag array based on the string it got as an
|
||||||
* argument. If the tag is invalid, <false> is returned. The tag
|
* argument. If the tag is invalid, <false> is returned. The tag
|
||||||
* attributes are extracted from the string and stored in the tag
|
* attributes are extracted from the string and stored in the tag
|
||||||
* array as an associative array.
|
* array as an associative array.
|
||||||
*
|
*
|
||||||
* @param string string to build tag from
|
* @param string string to build tag from
|
||||||
* @return array tag in array format
|
* @return array tag in array format
|
||||||
* @access private
|
* @access private
|
||||||
* @see _buildTagArray()
|
* @see _buildTagArray()
|
||||||
* @author Stijn de Reede <sjr@gmx.co.uk>
|
* @author Stijn de Reede <sjr@gmx.co.uk>
|
||||||
*/
|
*/
|
||||||
public function _buildTag($str)
|
public function _buildTag($str)
|
||||||
{
|
{
|
||||||
$tag = array('text' => $str, 'attributes' => array());
|
$tag = array('text' => $str, 'attributes' => array());
|
||||||
|
|
||||||
if (substr($str, 1, 1) == '/') { // closing tag
|
if (substr($str, 1, 1) == '/') { // closing tag
|
||||||
|
|
||||||
$tag['tag'] = strtolower(substr($str, 2, strlen($str) - 3));
|
$tag['tag'] = strtolower(substr($str, 2, strlen($str) - 3));
|
||||||
if (!in_array($tag['tag'], array_keys($this->_definedTags))) {
|
if (!in_array($tag['tag'], array_keys($this->_definedTags))) {
|
||||||
return false; // nope, it's not valid
|
return false; // nope, it's not valid
|
||||||
} else {
|
} else {
|
||||||
$tag['type'] = 2;
|
$tag['type'] = 2;
|
||||||
return $tag;
|
return $tag;
|
||||||
}
|
}
|
||||||
} else { // opening tag
|
} else { // opening tag
|
||||||
|
|
||||||
$tag['type'] = 1;
|
$tag['type'] = 1;
|
||||||
if (strpos($str, ' ') && (strpos($str, '=') === false)) {
|
if (strpos($str, ' ') && (strpos($str, '=') === false)) {
|
||||||
return false; // nope, it's not valid
|
return false; // nope, it's not valid
|
||||||
}
|
}
|
||||||
|
|
||||||
// tnx to Onno for the regex
|
// tnx to Onno for the regex
|
||||||
// split the tag with arguments and all
|
// split the tag with arguments and all
|
||||||
$oe = $this->_options['open_esc'];
|
$oe = $this->_options['open_esc'];
|
||||||
$ce = $this->_options['close_esc'];
|
$ce = $this->_options['close_esc'];
|
||||||
$tagArray = array();
|
$tagArray = array();
|
||||||
if (preg_match("!$oe([a-z0-9]+)[^$ce]*$ce!i", $str, $tagArray) == 0) {
|
if (preg_match("!$oe([a-z0-9]+)[^$ce]*$ce!i", $str, $tagArray) == 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$tag['tag'] = strtolower($tagArray[1]);
|
$tag['tag'] = strtolower($tagArray[1]);
|
||||||
if (!in_array($tag['tag'], array_keys($this->_definedTags))) {
|
if (!in_array($tag['tag'], array_keys($this->_definedTags))) {
|
||||||
return false; // nope, it's not valid
|
return false; // nope, it's not valid
|
||||||
}
|
}
|
||||||
|
|
||||||
// tnx to Onno for the regex
|
// tnx to Onno for the regex
|
||||||
// validate the arguments
|
// validate the arguments
|
||||||
$attributeArray = array();
|
$attributeArray = array();
|
||||||
$regex = "![\s$oe]([a-z0-9]+)=(\"[^\s$ce]+\"|[^\s$ce]";
|
$regex = "![\s$oe]([a-z0-9]+)=(\"[^\s$ce]+\"|[^\s$ce]";
|
||||||
if ($tag['tag'] != 'url') {
|
if ($tag['tag'] != 'url') {
|
||||||
$regex .= "[^=]";
|
$regex .= "[^=]";
|
||||||
}
|
}
|
||||||
$regex .= "+)(?=[\s$ce])!i";
|
$regex .= "+)(?=[\s$ce])!i";
|
||||||
preg_match_all($regex, $str, $attributeArray, PREG_SET_ORDER);
|
preg_match_all($regex, $str, $attributeArray, PREG_SET_ORDER);
|
||||||
foreach ($attributeArray as $attribute) {
|
foreach ($attributeArray as $attribute) {
|
||||||
$attNam = strtolower($attribute[1]);
|
$attNam = strtolower($attribute[1]);
|
||||||
if (in_array($attNam, array_keys($this->_definedTags[$tag['tag']]['attributes']))) {
|
if (in_array($attNam, array_keys($this->_definedTags[$tag['tag']]['attributes']))) {
|
||||||
if ($attribute[2][0] == '"' && $attribute[2][strlen($attribute[2])-1] == '"') {
|
if ($attribute[2][0] == '"' && $attribute[2][strlen($attribute[2])-1] == '"') {
|
||||||
$tag['attributes'][$attNam] = substr($attribute[2], 1, -1);
|
$tag['attributes'][$attNam] = substr($attribute[2], 1, -1);
|
||||||
} else {
|
} else {
|
||||||
$tag['attributes'][$attNam] = $attribute[2];
|
$tag['attributes'][$attNam] = $attribute[2];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return $tag;
|
return $tag;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates the tag array, regarding the allowed tags
|
* Validates the tag array, regarding the allowed tags
|
||||||
*
|
*
|
||||||
* While looping through the tag array, two following text tags are
|
* While looping through the tag array, two following text tags are
|
||||||
* joined, and it is checked that the tag is allowed inside the
|
* joined, and it is checked that the tag is allowed inside the
|
||||||
* last opened tag.
|
* last opened tag.
|
||||||
* By remembering what tags have been opened it is checked that
|
* By remembering what tags have been opened it is checked that
|
||||||
* there is correct (xml compliant) nesting.
|
* there is correct (xml compliant) nesting.
|
||||||
* In the end all still opened tags are closed.
|
* In the end all still opened tags are closed.
|
||||||
*
|
*
|
||||||
* @return none
|
* @return none
|
||||||
* @access private
|
* @access private
|
||||||
* @see _isAllowed()
|
* @see _isAllowed()
|
||||||
* @see $_tagArray
|
* @see $_tagArray
|
||||||
* @author Stijn de Reede <sjr@gmx.co.uk>, Seth Price <seth@pricepages.org>
|
* @author Stijn de Reede <sjr@gmx.co.uk>, Seth Price <seth@pricepages.org>
|
||||||
*/
|
*/
|
||||||
public function _validateTagArray()
|
public function _validateTagArray()
|
||||||
{
|
{
|
||||||
$newTagArray = array();
|
$newTagArray = array();
|
||||||
$openTags = array();
|
$openTags = array();
|
||||||
foreach ($this->_tagArray as $tag) {
|
foreach ($this->_tagArray as $tag) {
|
||||||
$prevTag = end($newTagArray);
|
$prevTag = end($newTagArray);
|
||||||
switch ($tag['type']) {
|
switch ($tag['type']) {
|
||||||
case 0:
|
case 0:
|
||||||
if (($child = $this->_childNeeded(end($openTags), 'text')) &&
|
if (($child = $this->_childNeeded(end($openTags), 'text')) &&
|
||||||
$child !== false &&
|
$child !== false &&
|
||||||
/*
|
/*
|
||||||
* No idea what to do in this case: A child is needed, but
|
* No idea what to do in this case: A child is needed, but
|
||||||
* no valid one is returned. We'll ignore it here and live
|
* no valid one is returned. We'll ignore it here and live
|
||||||
* with it until someone reports a valid bug.
|
* with it until someone reports a valid bug.
|
||||||
*/
|
*/
|
||||||
$child !== true )
|
$child !== true )
|
||||||
{
|
{
|
||||||
if (trim($tag['text']) == '') {
|
if (trim($tag['text']) == '') {
|
||||||
//just an empty indentation or newline without value?
|
//just an empty indentation or newline without value?
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$newTagArray[] = $child;
|
$newTagArray[] = $child;
|
||||||
$openTags[] = $child['tag'];
|
$openTags[] = $child['tag'];
|
||||||
}
|
}
|
||||||
if ($prevTag['type'] === 0) {
|
if ($prevTag['type'] === 0) {
|
||||||
$tag['text'] = $prevTag['text'].$tag['text'];
|
$tag['text'] = $prevTag['text'].$tag['text'];
|
||||||
array_pop($newTagArray);
|
array_pop($newTagArray);
|
||||||
}
|
}
|
||||||
$newTagArray[] = $tag;
|
$newTagArray[] = $tag;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
if (!$this->_isAllowed(end($openTags), $tag['tag']) ||
|
if (!$this->_isAllowed(end($openTags), $tag['tag']) ||
|
||||||
($parent = $this->_parentNeeded(end($openTags), $tag['tag'])) === true ||
|
($parent = $this->_parentNeeded(end($openTags), $tag['tag'])) === true ||
|
||||||
($child = $this->_childNeeded(end($openTags), $tag['tag'])) === true) {
|
($child = $this->_childNeeded(end($openTags), $tag['tag'])) === true) {
|
||||||
$tag['type'] = 0;
|
$tag['type'] = 0;
|
||||||
if ($prevTag['type'] === 0) {
|
if ($prevTag['type'] === 0) {
|
||||||
$tag['text'] = $prevTag['text'].$tag['text'];
|
$tag['text'] = $prevTag['text'].$tag['text'];
|
||||||
array_pop($newTagArray);
|
array_pop($newTagArray);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if ($parent) {
|
if ($parent) {
|
||||||
/*
|
/*
|
||||||
* Avoid use of parent if we can help it. If we are
|
* Avoid use of parent if we can help it. If we are
|
||||||
* trying to insert a new parent, but the current tag is
|
* trying to insert a new parent, but the current tag is
|
||||||
* the same as the previous tag, then assume that the
|
* the same as the previous tag, then assume that the
|
||||||
* previous tag structure is valid, and add this tag as
|
* previous tag structure is valid, and add this tag as
|
||||||
* a sibling. To add as a sibling, we need to close the
|
* a sibling. To add as a sibling, we need to close the
|
||||||
* current tag.
|
* current tag.
|
||||||
*/
|
*/
|
||||||
if ($tag['tag'] == end($openTags)){
|
if ($tag['tag'] == end($openTags)){
|
||||||
$newTagArray[] = $this->_buildTag('[/'.$tag['tag'].']');
|
$newTagArray[] = $this->_buildTag('[/'.$tag['tag'].']');
|
||||||
array_pop($openTags);
|
array_pop($openTags);
|
||||||
} else {
|
} else {
|
||||||
$newTagArray[] = $parent;
|
$newTagArray[] = $parent;
|
||||||
$openTags[] = $parent['tag'];
|
$openTags[] = $parent['tag'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($child) {
|
if ($child) {
|
||||||
$newTagArray[] = $child;
|
$newTagArray[] = $child;
|
||||||
$openTags[] = $child['tag'];
|
$openTags[] = $child['tag'];
|
||||||
}
|
}
|
||||||
$openTags[] = $tag['tag'];
|
$openTags[] = $tag['tag'];
|
||||||
}
|
}
|
||||||
$newTagArray[] = $tag;
|
$newTagArray[] = $tag;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
if (($tag['tag'] == end($openTags) || $this->_isAllowed(end($openTags), $tag['tag']))) {
|
if (($tag['tag'] == end($openTags) || $this->_isAllowed(end($openTags), $tag['tag']))) {
|
||||||
if (in_array($tag['tag'], $openTags)) {
|
if (in_array($tag['tag'], $openTags)) {
|
||||||
$tmpOpenTags = array();
|
$tmpOpenTags = array();
|
||||||
while (end($openTags) != $tag['tag']) {
|
while (end($openTags) != $tag['tag']) {
|
||||||
$newTagArray[] = $this->_buildTag('[/'.end($openTags).']');
|
$newTagArray[] = $this->_buildTag('[/'.end($openTags).']');
|
||||||
$tmpOpenTags[] = end($openTags);
|
$tmpOpenTags[] = end($openTags);
|
||||||
array_pop($openTags);
|
array_pop($openTags);
|
||||||
}
|
}
|
||||||
$newTagArray[] = $tag;
|
$newTagArray[] = $tag;
|
||||||
array_pop($openTags);
|
array_pop($openTags);
|
||||||
/* why is this here? it just seems to break things
|
/* why is this here? it just seems to break things
|
||||||
* (nested lists where closing tags need to be
|
* (nested lists where closing tags need to be
|
||||||
* generated)
|
* generated)
|
||||||
while (end($tmpOpenTags)) {
|
while (end($tmpOpenTags)) {
|
||||||
$tmpTag = $this->_buildTag('['.end($tmpOpenTags).']');
|
$tmpTag = $this->_buildTag('['.end($tmpOpenTags).']');
|
||||||
$newTagArray[] = $tmpTag;
|
$newTagArray[] = $tmpTag;
|
||||||
$openTags[] = $tmpTag['tag'];
|
$openTags[] = $tmpTag['tag'];
|
||||||
array_pop($tmpOpenTags);
|
array_pop($tmpOpenTags);
|
||||||
}*/
|
}*/
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$tag['type'] = 0;
|
$tag['type'] = 0;
|
||||||
if ($prevTag['type'] === 0) {
|
if ($prevTag['type'] === 0) {
|
||||||
$tag['text'] = $prevTag['text'].$tag['text'];
|
$tag['text'] = $prevTag['text'].$tag['text'];
|
||||||
array_pop($newTagArray);
|
array_pop($newTagArray);
|
||||||
}
|
}
|
||||||
$newTagArray[] = $tag;
|
$newTagArray[] = $tag;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while (end($openTags)) {
|
while (end($openTags)) {
|
||||||
$newTagArray[] = $this->_buildTag('[/'.end($openTags).']');
|
$newTagArray[] = $this->_buildTag('[/'.end($openTags).']');
|
||||||
array_pop($openTags);
|
array_pop($openTags);
|
||||||
}
|
}
|
||||||
$this->_tagArray = $newTagArray;
|
$this->_tagArray = $newTagArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks to see if a parent is needed
|
* Checks to see if a parent is needed
|
||||||
*
|
*
|
||||||
* Checks to see if the current $in tag has an appropriate parent. If it
|
* Checks to see if the current $in tag has an appropriate parent. If it
|
||||||
* does, then it returns false. If a parent is needed, then it returns the
|
* does, then it returns false. If a parent is needed, then it returns the
|
||||||
* first tag in the list to add to the stack.
|
* first tag in the list to add to the stack.
|
||||||
*
|
*
|
||||||
* @param array tag that is on the outside
|
* @param array tag that is on the outside
|
||||||
* @param array tag that is on the inside
|
* @param array tag that is on the inside
|
||||||
* @return boolean false if not needed, tag if needed, true if out
|
* @return boolean false if not needed, tag if needed, true if out
|
||||||
* of our minds
|
* of our minds
|
||||||
* @access private
|
* @access private
|
||||||
* @see _validateTagArray()
|
* @see _validateTagArray()
|
||||||
* @author Seth Price <seth@pricepages.org>
|
* @author Seth Price <seth@pricepages.org>
|
||||||
*/
|
*/
|
||||||
public function _parentNeeded($out, $in)
|
public function _parentNeeded($out, $in)
|
||||||
{
|
{
|
||||||
if (!isset($this->_definedTags[$in]['parent']) ||
|
if (!isset($this->_definedTags[$in]['parent']) ||
|
||||||
($this->_definedTags[$in]['parent'] == 'all')
|
($this->_definedTags[$in]['parent'] == 'all')
|
||||||
) {
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$ar = explode('^', $this->_definedTags[$in]['parent']);
|
$ar = explode('^', $this->_definedTags[$in]['parent']);
|
||||||
$tags = explode(',', $ar[1]);
|
$tags = explode(',', $ar[1]);
|
||||||
if ($ar[0] == 'none'){
|
if ($ar[0] == 'none'){
|
||||||
if ($out && in_array($out, $tags)) {
|
if ($out && in_array($out, $tags)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
//Create a tag from the first one on the list
|
//Create a tag from the first one on the list
|
||||||
return $this->_buildTag('['.$tags[0].']');
|
return $this->_buildTag('['.$tags[0].']');
|
||||||
}
|
}
|
||||||
if ($ar[0] == 'all' && $out && !in_array($out, $tags)) {
|
if ($ar[0] == 'all' && $out && !in_array($out, $tags)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Tag is needed, we don't know which one. We could make something up,
|
// Tag is needed, we don't know which one. We could make something up,
|
||||||
// but it would be so random, I think that it would be worthless.
|
// but it would be so random, I think that it would be worthless.
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks to see if a child is needed
|
* Checks to see if a child is needed
|
||||||
*
|
*
|
||||||
* Checks to see if the current $out tag has an appropriate child. If it
|
* Checks to see if the current $out tag has an appropriate child. If it
|
||||||
* does, then it returns false. If a child is needed, then it returns the
|
* does, then it returns false. If a child is needed, then it returns the
|
||||||
* first tag in the list to add to the stack.
|
* first tag in the list to add to the stack.
|
||||||
*
|
*
|
||||||
* @param array tag that is on the outside
|
* @param array tag that is on the outside
|
||||||
* @param array tag that is on the inside
|
* @param array tag that is on the inside
|
||||||
* @return boolean false if not needed, tag if needed, true if out
|
* @return boolean false if not needed, tag if needed, true if out
|
||||||
* of our minds
|
* of our minds
|
||||||
* @access private
|
* @access private
|
||||||
* @see _validateTagArray()
|
* @see _validateTagArray()
|
||||||
* @author Seth Price <seth@pricepages.org>
|
* @author Seth Price <seth@pricepages.org>
|
||||||
*/
|
*/
|
||||||
public function _childNeeded($out, $in)
|
public function _childNeeded($out, $in)
|
||||||
{
|
{
|
||||||
if (!isset($this->_definedTags[$out]['child']) ||
|
if (!isset($this->_definedTags[$out]['child']) ||
|
||||||
($this->_definedTags[$out]['child'] == 'all')
|
($this->_definedTags[$out]['child'] == 'all')
|
||||||
) {
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$ar = explode('^', $this->_definedTags[$out]['child']);
|
$ar = explode('^', $this->_definedTags[$out]['child']);
|
||||||
$tags = explode(',', $ar[1]);
|
$tags = explode(',', $ar[1]);
|
||||||
if ($ar[0] == 'none'){
|
if ($ar[0] == 'none'){
|
||||||
if ($in && in_array($in, $tags)) {
|
if ($in && in_array($in, $tags)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
//Create a tag from the first one on the list
|
//Create a tag from the first one on the list
|
||||||
return $this->_buildTag('['.$tags[0].']');
|
return $this->_buildTag('['.$tags[0].']');
|
||||||
}
|
}
|
||||||
if ($ar[0] == 'all' && $in && !in_array($in, $tags)) {
|
if ($ar[0] == 'all' && $in && !in_array($in, $tags)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Tag is needed, we don't know which one. We could make something up,
|
// Tag is needed, we don't know which one. We could make something up,
|
||||||
// but it would be so random, I think that it would be worthless.
|
// but it would be so random, I think that it would be worthless.
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks to see if a tag is allowed inside another tag
|
* Checks to see if a tag is allowed inside another tag
|
||||||
*
|
*
|
||||||
* The allowed tags are extracted from the private _definedTags array.
|
* The allowed tags are extracted from the private _definedTags array.
|
||||||
*
|
*
|
||||||
* @param array tag that is on the outside
|
* @param array tag that is on the outside
|
||||||
* @param array tag that is on the inside
|
* @param array tag that is on the inside
|
||||||
* @return boolean return true if the tag is allowed, false
|
* @return boolean return true if the tag is allowed, false
|
||||||
* otherwise
|
* otherwise
|
||||||
* @access private
|
* @access private
|
||||||
* @see _validateTagArray()
|
* @see _validateTagArray()
|
||||||
* @author Stijn de Reede <sjr@gmx.co.uk>
|
* @author Stijn de Reede <sjr@gmx.co.uk>
|
||||||
*/
|
*/
|
||||||
public function _isAllowed($out, $in)
|
public function _isAllowed($out, $in)
|
||||||
{
|
{
|
||||||
if (!$out || ($this->_definedTags[$out]['allowed'] == 'all')) {
|
if (!$out || ($this->_definedTags[$out]['allowed'] == 'all')) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if ($this->_definedTags[$out]['allowed'] == 'none') {
|
if ($this->_definedTags[$out]['allowed'] == 'none') {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$ar = explode('^', $this->_definedTags[$out]['allowed']);
|
$ar = explode('^', $this->_definedTags[$out]['allowed']);
|
||||||
$tags = explode(',', $ar[1]);
|
$tags = explode(',', $ar[1]);
|
||||||
if ($ar[0] == 'none' && in_array($in, $tags)) {
|
if ($ar[0] == 'none' && in_array($in, $tags)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if ($ar[0] == 'all' && in_array($in, $tags)) {
|
if ($ar[0] == 'all' && in_array($in, $tags)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds a parsed string based on the tag array
|
* Builds a parsed string based on the tag array
|
||||||
*
|
*
|
||||||
* The correct html and attribute values are extracted from the private
|
* The correct html and attribute values are extracted from the private
|
||||||
* _definedTags array.
|
* _definedTags array.
|
||||||
*
|
*
|
||||||
* @return none
|
* @return none
|
||||||
* @access private
|
* @access private
|
||||||
* @see $_tagArray
|
* @see $_tagArray
|
||||||
* @see $_parsed
|
* @see $_parsed
|
||||||
* @author Stijn de Reede <sjr@gmx.co.uk>
|
* @author Stijn de Reede <sjr@gmx.co.uk>
|
||||||
*/
|
*/
|
||||||
public function _buildParsedString()
|
public function _buildParsedString()
|
||||||
{
|
{
|
||||||
$this->_parsed = '';
|
$this->_parsed = '';
|
||||||
foreach ($this->_tagArray as $tag) {
|
foreach ($this->_tagArray as $tag) {
|
||||||
switch ($tag['type']) {
|
switch ($tag['type']) {
|
||||||
|
|
||||||
// just text
|
// just text
|
||||||
case 0:
|
case 0:
|
||||||
$this->_parsed .= $tag['text'];
|
$this->_parsed .= $tag['text'];
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// opening tag
|
// opening tag
|
||||||
case 1:
|
case 1:
|
||||||
$this->_parsed .= '<'.$this->_definedTags[$tag['tag']]['htmlopen'];
|
$this->_parsed .= '<'.$this->_definedTags[$tag['tag']]['htmlopen'];
|
||||||
if ($this->_options['quotestyle'] == 'single') $q = "'";
|
if ($this->_options['quotestyle'] == 'single') $q = "'";
|
||||||
if ($this->_options['quotestyle'] == 'double') $q = '"';
|
if ($this->_options['quotestyle'] == 'double') $q = '"';
|
||||||
foreach ($tag['attributes'] as $a => $v) {
|
foreach ($tag['attributes'] as $a => $v) {
|
||||||
//prevent XSS attacks. IMHO this is not enough, though...
|
//prevent XSS attacks. IMHO this is not enough, though...
|
||||||
//@see http://pear.php.net/bugs/bug.php?id=5609
|
//@see http://pear.php.net/bugs/bug.php?id=5609
|
||||||
$v = preg_replace('#(script|about|applet|activex|chrome):#is', "\\1:", $v);
|
$v = preg_replace('#(script|about|applet|activex|chrome):#is', "\\1:", $v);
|
||||||
$v = htmlspecialchars($v);
|
$v = htmlspecialchars($v);
|
||||||
$v = str_replace('&amp;', '&', $v);
|
$v = str_replace('&amp;', '&', $v);
|
||||||
|
|
||||||
if (($this->_options['quotewhat'] == 'nothing') ||
|
if (($this->_options['quotewhat'] == 'nothing') ||
|
||||||
(($this->_options['quotewhat'] == 'strings') && is_numeric($v))
|
(($this->_options['quotewhat'] == 'strings') && is_numeric($v))
|
||||||
) {
|
) {
|
||||||
$this->_parsed .= ' '.sprintf($this->_definedTags[$tag['tag']]['attributes'][$a], $v, '');
|
$this->_parsed .= ' '.sprintf($this->_definedTags[$tag['tag']]['attributes'][$a], $v, '');
|
||||||
} else {
|
} else {
|
||||||
$this->_parsed .= ' '.sprintf($this->_definedTags[$tag['tag']]['attributes'][$a], $v, $q);
|
$this->_parsed .= ' '.sprintf($this->_definedTags[$tag['tag']]['attributes'][$a], $v, $q);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($this->_definedTags[$tag['tag']]['htmlclose'] == '' && $this->_options['xmlclose']) {
|
if ($this->_definedTags[$tag['tag']]['htmlclose'] == '' && $this->_options['xmlclose']) {
|
||||||
$this->_parsed .= ' /';
|
$this->_parsed .= ' /';
|
||||||
}
|
}
|
||||||
$this->_parsed .= '>';
|
$this->_parsed .= '>';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// closing tag
|
// closing tag
|
||||||
case 2:
|
case 2:
|
||||||
if ($this->_definedTags[$tag['tag']]['htmlclose'] != '') {
|
if ($this->_definedTags[$tag['tag']]['htmlclose'] != '') {
|
||||||
$this->_parsed .= '</'.$this->_definedTags[$tag['tag']]['htmlclose'].'>';
|
$this->_parsed .= '</'.$this->_definedTags[$tag['tag']]['htmlclose'].'>';
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets text in the object to be parsed
|
* Sets text in the object to be parsed
|
||||||
*
|
*
|
||||||
* @param string the text to set in the object
|
* @param string the text to set in the object
|
||||||
* @return none
|
* @return none
|
||||||
* @access public
|
* @access public
|
||||||
* @see getText()
|
* @see getText()
|
||||||
* @see $_text
|
* @see $_text
|
||||||
* @author Stijn de Reede <sjr@gmx.co.uk>
|
* @author Stijn de Reede <sjr@gmx.co.uk>
|
||||||
*/
|
*/
|
||||||
public function setText($str)
|
public function setText($str)
|
||||||
{
|
{
|
||||||
$this->_text = $str;
|
$this->_text = $str;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the unparsed text from the object
|
* Gets the unparsed text from the object
|
||||||
*
|
*
|
||||||
* @return string the text set in the object
|
* @return string the text set in the object
|
||||||
* @access public
|
* @access public
|
||||||
* @see setText()
|
* @see setText()
|
||||||
* @see $_text
|
* @see $_text
|
||||||
* @author Stijn de Reede <sjr@gmx.co.uk>
|
* @author Stijn de Reede <sjr@gmx.co.uk>
|
||||||
*/
|
*/
|
||||||
public function getText()
|
public function getText()
|
||||||
{
|
{
|
||||||
return $this->_text;
|
return $this->_text;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the preparsed text from the object
|
* Gets the preparsed text from the object
|
||||||
*
|
*
|
||||||
* @return string the text set in the object
|
* @return string the text set in the object
|
||||||
* @access public
|
* @access public
|
||||||
* @see _preparse()
|
* @see _preparse()
|
||||||
* @see $_preparsed
|
* @see $_preparsed
|
||||||
* @author Stijn de Reede <sjr@gmx.co.uk>
|
* @author Stijn de Reede <sjr@gmx.co.uk>
|
||||||
*/
|
*/
|
||||||
public function getPreparsed()
|
public function getPreparsed()
|
||||||
{
|
{
|
||||||
return $this->_preparsed;
|
return $this->_preparsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the parsed text from the object
|
* Gets the parsed text from the object
|
||||||
*
|
*
|
||||||
* @return string the parsed text set in the object
|
* @return string the parsed text set in the object
|
||||||
* @access public
|
* @access public
|
||||||
* @see parse()
|
* @see parse()
|
||||||
* @see $_parsed
|
* @see $_parsed
|
||||||
* @author Stijn de Reede <sjr@gmx.co.uk>
|
* @author Stijn de Reede <sjr@gmx.co.uk>
|
||||||
*/
|
*/
|
||||||
public function getParsed()
|
public function getParsed()
|
||||||
{
|
{
|
||||||
return $this->_parsed;
|
return $this->_parsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the text set in the object
|
* Parses the text set in the object
|
||||||
*
|
*
|
||||||
* @return none
|
* @return none
|
||||||
* @access public
|
* @access public
|
||||||
* @see _preparse()
|
* @see _preparse()
|
||||||
* @see _buildTagArray()
|
* @see _buildTagArray()
|
||||||
* @see _validateTagArray()
|
* @see _validateTagArray()
|
||||||
* @see _buildParsedString()
|
* @see _buildParsedString()
|
||||||
* @author Stijn de Reede <sjr@gmx.co.uk>
|
* @author Stijn de Reede <sjr@gmx.co.uk>
|
||||||
*/
|
*/
|
||||||
public function parse()
|
public function parse()
|
||||||
{
|
{
|
||||||
$this->_preparse();
|
$this->_preparse();
|
||||||
$this->_buildTagArray();
|
$this->_buildTagArray();
|
||||||
$this->_validateTagArray();
|
$this->_validateTagArray();
|
||||||
$this->_buildParsedString();
|
$this->_buildParsedString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Quick method to do setText(), parse() and getParsed at once
|
* Quick method to do setText(), parse() and getParsed at once
|
||||||
*
|
*
|
||||||
* @return none
|
* @return none
|
||||||
* @access public
|
* @access public
|
||||||
* @see parse()
|
* @see parse()
|
||||||
* @see $_text
|
* @see $_text
|
||||||
* @author Stijn de Reede <sjr@gmx.co.uk>
|
* @author Stijn de Reede <sjr@gmx.co.uk>
|
||||||
*/
|
*/
|
||||||
public function qparse($str)
|
public function qparse($str)
|
||||||
{
|
{
|
||||||
$this->_text = $str;
|
$this->_text = $str;
|
||||||
$this->parse();
|
$this->parse();
|
||||||
return $this->_parsed;
|
return $this->_parsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Quick static method to do setText(), parse() and getParsed at once
|
* Quick static method to do setText(), parse() and getParsed at once
|
||||||
*
|
*
|
||||||
* @return none
|
* @return none
|
||||||
* @access public
|
* @access public
|
||||||
* @see parse()
|
* @see parse()
|
||||||
* @see $_text
|
* @see $_text
|
||||||
* @author Stijn de Reede <sjr@gmx.co.uk>
|
* @author Stijn de Reede <sjr@gmx.co.uk>
|
||||||
*/
|
*/
|
||||||
public function staticQparse($str)
|
public function staticQparse($str)
|
||||||
{
|
{
|
||||||
$p = new SSHTMLBBCodeParser();
|
$p = new SSHTMLBBCodeParser();
|
||||||
$str = $p->qparse($str);
|
$str = $p->qparse($str);
|
||||||
unset($p);
|
unset($p);
|
||||||
return $str;
|
return $str;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -147,7 +147,7 @@ class SearchContext extends Object {
|
|||||||
$searchParamArray = $searchParams;
|
$searchParamArray = $searchParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach($searchParamArray as $key => $value) {
|
foreach($searchParamArray as $key => $value) {
|
||||||
$key = str_replace('__', '.', $key);
|
$key = str_replace('__', '.', $key);
|
||||||
if($filter = $this->getFilter($key)) {
|
if($filter = $this->getFilter($key)) {
|
||||||
$filter->setModel($this->modelClass);
|
$filter->setModel($this->modelClass);
|
||||||
@ -158,9 +158,9 @@ class SearchContext extends Object {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if($this->connective != "AND") {
|
if($this->connective != "AND") {
|
||||||
throw new Exception("SearchContext connective '$this->connective' not supported after ORM-rewrite.");
|
throw new Exception("SearchContext connective '$this->connective' not supported after ORM-rewrite.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return $query;
|
return $query;
|
||||||
}
|
}
|
||||||
|
@ -11,95 +11,95 @@
|
|||||||
*/
|
*/
|
||||||
abstract class Authenticator extends Object {
|
abstract class Authenticator extends Object {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This variable holds all authenticators that should be used
|
* This variable holds all authenticators that should be used
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
private static $authenticators = array('MemberAuthenticator');
|
private static $authenticators = array('MemberAuthenticator');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to influence the order of authenticators on the login-screen
|
* Used to influence the order of authenticators on the login-screen
|
||||||
* (default shows first).
|
* (default shows first).
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
private static $default_authenticator = 'MemberAuthenticator';
|
private static $default_authenticator = 'MemberAuthenticator';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method to authenticate an user
|
* Method to authenticate an user
|
||||||
*
|
*
|
||||||
* @param array $RAW_data Raw data to authenticate the user
|
* @param array $RAW_data Raw data to authenticate the user
|
||||||
* @param Form $form Optional: If passed, better error messages can be
|
* @param Form $form Optional: If passed, better error messages can be
|
||||||
* produced by using
|
* produced by using
|
||||||
* {@link Form::sessionMessage()}
|
* {@link Form::sessionMessage()}
|
||||||
* @return bool|Member Returns FALSE if authentication fails, otherwise
|
* @return bool|Member Returns FALSE if authentication fails, otherwise
|
||||||
* the member object
|
* the member object
|
||||||
*/
|
*/
|
||||||
public static function authenticate($RAW_data, Form $form = null) {
|
public static function authenticate($RAW_data, Form $form = null) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method that creates the login form for this authentication method
|
* Method that creates the login form for this authentication method
|
||||||
*
|
*
|
||||||
* @param Controller The parent controller, necessary to create the
|
* @param Controller The parent controller, necessary to create the
|
||||||
* appropriate form action tag
|
* appropriate form action tag
|
||||||
* @return Form Returns the login form to use with this authentication
|
* @return Form Returns the login form to use with this authentication
|
||||||
* method
|
* method
|
||||||
*/
|
*/
|
||||||
public static function get_login_form(Controller $controller) {
|
public static function get_login_form(Controller $controller) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the name of the authentication method
|
* Get the name of the authentication method
|
||||||
*
|
*
|
||||||
* @return string Returns the name of the authentication method.
|
* @return string Returns the name of the authentication method.
|
||||||
*/
|
*/
|
||||||
public static function get_name() {
|
public static function get_name() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function register($authenticator) {
|
public static function register($authenticator) {
|
||||||
self::register_authenticator($authenticator);
|
self::register_authenticator($authenticator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register a new authenticator
|
* Register a new authenticator
|
||||||
*
|
*
|
||||||
* The new authenticator has to exist and to be derived from the
|
* The new authenticator has to exist and to be derived from the
|
||||||
* {@link Authenticator}.
|
* {@link Authenticator}.
|
||||||
* Every authenticator can be registered only once.
|
* Every authenticator can be registered only once.
|
||||||
*
|
*
|
||||||
* @param string $authenticator Name of the authenticator class to
|
* @param string $authenticator Name of the authenticator class to
|
||||||
* register
|
* register
|
||||||
* @return bool Returns TRUE on success, FALSE otherwise.
|
* @return bool Returns TRUE on success, FALSE otherwise.
|
||||||
*/
|
*/
|
||||||
public static function register_authenticator($authenticator) {
|
public static function register_authenticator($authenticator) {
|
||||||
$authenticator = trim($authenticator);
|
$authenticator = trim($authenticator);
|
||||||
|
|
||||||
if(class_exists($authenticator) == false)
|
if(class_exists($authenticator) == false)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if(is_subclass_of($authenticator, 'Authenticator') == false)
|
if(is_subclass_of($authenticator, 'Authenticator') == false)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if(in_array($authenticator, self::$authenticators) == false) {
|
if(in_array($authenticator, self::$authenticators) == false) {
|
||||||
if(call_user_func(array($authenticator, 'on_register')) === true) {
|
if(call_user_func(array($authenticator, 'on_register')) === true) {
|
||||||
array_push(self::$authenticators, $authenticator);
|
array_push(self::$authenticators, $authenticator);
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function unregister($authenticator) {
|
public static function unregister($authenticator) {
|
||||||
self::unregister_authenticator($authenticator);
|
self::unregister_authenticator($authenticator);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove a previously registered authenticator
|
* Remove a previously registered authenticator
|
||||||
*
|
*
|
||||||
@ -108,82 +108,82 @@ abstract class Authenticator extends Object {
|
|||||||
*/
|
*/
|
||||||
public static function unregister_authenticator($authenticator) {
|
public static function unregister_authenticator($authenticator) {
|
||||||
if(call_user_func(array($authenticator, 'on_unregister')) === true) {
|
if(call_user_func(array($authenticator, 'on_unregister')) === true) {
|
||||||
if(in_array($authenticator, self::$authenticators)) {
|
if(in_array($authenticator, self::$authenticators)) {
|
||||||
unset(self::$authenticators[array_search($authenticator, self::$authenticators)]);
|
unset(self::$authenticators[array_search($authenticator, self::$authenticators)]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a given authenticator is registered
|
* Check if a given authenticator is registered
|
||||||
*
|
*
|
||||||
* @param string $authenticator Name of the authenticator class to check
|
* @param string $authenticator Name of the authenticator class to check
|
||||||
* @return bool Returns TRUE if the authenticator is registered, FALSE
|
* @return bool Returns TRUE if the authenticator is registered, FALSE
|
||||||
* otherwise.
|
* otherwise.
|
||||||
*/
|
*/
|
||||||
public static function is_registered($authenticator) {
|
public static function is_registered($authenticator) {
|
||||||
return in_array($authenticator, self::$authenticators);
|
return in_array($authenticator, self::$authenticators);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all registered authenticators
|
* Get all registered authenticators
|
||||||
*
|
*
|
||||||
* @return array Returns an array with the class names of all registered
|
* @return array Returns an array with the class names of all registered
|
||||||
* authenticators.
|
* authenticators.
|
||||||
*/
|
*/
|
||||||
public static function get_authenticators() {
|
public static function get_authenticators() {
|
||||||
// put default authenticator first (mainly for tab-order on loginform)
|
// put default authenticator first (mainly for tab-order on loginform)
|
||||||
if($key = array_search(self::$default_authenticator,self::$authenticators)) {
|
if($key = array_search(self::$default_authenticator,self::$authenticators)) {
|
||||||
unset(self::$authenticators[$key]);
|
unset(self::$authenticators[$key]);
|
||||||
array_unshift(self::$authenticators, self::$default_authenticator);
|
array_unshift(self::$authenticators, self::$default_authenticator);
|
||||||
}
|
}
|
||||||
|
|
||||||
return self::$authenticators;
|
return self::$authenticators;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a default authenticator (shows first in tabs)
|
* Set a default authenticator (shows first in tabs)
|
||||||
*
|
*
|
||||||
* @param string
|
* @param string
|
||||||
*/
|
*/
|
||||||
public static function set_default_authenticator($authenticator) {
|
public static function set_default_authenticator($authenticator) {
|
||||||
self::$default_authenticator = $authenticator;
|
self::$default_authenticator = $authenticator;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function get_default_authenticator() {
|
public static function get_default_authenticator() {
|
||||||
return self::$default_authenticator;
|
return self::$default_authenticator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback function that is called when the authenticator is registered
|
* Callback function that is called when the authenticator is registered
|
||||||
*
|
*
|
||||||
* Use this method for initialization of a newly registered authenticator.
|
* Use this method for initialization of a newly registered authenticator.
|
||||||
* Just overload this method and it will be called when the authenticator
|
* Just overload this method and it will be called when the authenticator
|
||||||
* is registered.
|
* is registered.
|
||||||
* <b>If the method returns FALSE, the authenticator won't be
|
* <b>If the method returns FALSE, the authenticator won't be
|
||||||
* registered!</b>
|
* registered!</b>
|
||||||
*
|
*
|
||||||
* @return bool Returns TRUE on success, FALSE otherwise.
|
* @return bool Returns TRUE on success, FALSE otherwise.
|
||||||
*/
|
*/
|
||||||
protected static function on_register() {
|
protected static function on_register() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback function that is called when an authenticator is removed.
|
* Callback function that is called when an authenticator is removed.
|
||||||
*
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
protected static function on_unregister() {
|
protected static function on_unregister() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,7 +154,7 @@ class Group extends DataObject {
|
|||||||
// but tabstrip.js doesn't display tabs when directly adressed through a URL pragma
|
// but tabstrip.js doesn't display tabs when directly adressed through a URL pragma
|
||||||
_t('Group.RolesAddEditLink', 'Manage roles')
|
_t('Group.RolesAddEditLink', 'Manage roles')
|
||||||
) .
|
) .
|
||||||
"</p>"
|
"</p>"
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -315,7 +315,7 @@ class Group extends DataObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getTreeTitle() {
|
public function getTreeTitle() {
|
||||||
if($this->hasMethod('alternateTreeTitle')) return $this->alternateTreeTitle();
|
if($this->hasMethod('alternateTreeTitle')) return $this->alternateTreeTitle();
|
||||||
else return htmlspecialchars($this->Title, ENT_QUOTES);
|
else return htmlspecialchars($this->Title, ENT_QUOTES);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -367,7 +367,7 @@ class Group extends DataObject {
|
|||||||
$results = $this->extend('canEdit', $member);
|
$results = $this->extend('canEdit', $member);
|
||||||
if($results && is_array($results)) if(!min($results)) return false;
|
if($results && is_array($results)) if(!min($results)) return false;
|
||||||
|
|
||||||
if(
|
if(
|
||||||
// either we have an ADMIN
|
// either we have an ADMIN
|
||||||
(bool)Permission::checkMember($member, "ADMIN")
|
(bool)Permission::checkMember($member, "ADMIN")
|
||||||
|| (
|
|| (
|
||||||
|
@ -561,7 +561,7 @@ class Member extends DataObject implements TemplateGlobalProvider {
|
|||||||
* It should return fields that are editable by the admin and the logged-in user.
|
* It should return fields that are editable by the admin and the logged-in user.
|
||||||
*
|
*
|
||||||
* @return FieldList Returns a {@link FieldList} containing the fields for
|
* @return FieldList Returns a {@link FieldList} containing the fields for
|
||||||
* the member form.
|
* the member form.
|
||||||
*/
|
*/
|
||||||
public function getMemberFormFields() {
|
public function getMemberFormFields() {
|
||||||
$fields = parent::getFrontendFields();
|
$fields = parent::getFrontendFields();
|
||||||
@ -658,10 +658,10 @@ class Member extends DataObject implements TemplateGlobalProvider {
|
|||||||
|
|
||||||
return $word . $number;
|
return $word . $number;
|
||||||
} else {
|
} else {
|
||||||
$random = rand();
|
$random = rand();
|
||||||
$string = md5($random);
|
$string = md5($random);
|
||||||
$output = substr($string, 0, 6);
|
$output = substr($string, 0, 6);
|
||||||
return $output;
|
return $output;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1149,7 +1149,7 @@ class Member extends DataObject implements TemplateGlobalProvider {
|
|||||||
*
|
*
|
||||||
* @param array $groupList An array of group code names.
|
* @param array $groupList An array of group code names.
|
||||||
* @param array $memberGroups A component set of groups (if set to NULL,
|
* @param array $memberGroups A component set of groups (if set to NULL,
|
||||||
* $this->groups() will be used)
|
* $this->groups() will be used)
|
||||||
* @return array Groups in which the member is NOT in.
|
* @return array Groups in which the member is NOT in.
|
||||||
*/
|
*/
|
||||||
public function memberNotInGroups($groupList, $memberGroups = null){
|
public function memberNotInGroups($groupList, $memberGroups = null){
|
||||||
@ -1171,7 +1171,7 @@ class Member extends DataObject implements TemplateGlobalProvider {
|
|||||||
* this member.
|
* this member.
|
||||||
*
|
*
|
||||||
* @return FieldList Return a FieldList of fields that would appropriate for
|
* @return FieldList Return a FieldList of fields that would appropriate for
|
||||||
* editing this member.
|
* editing this member.
|
||||||
*/
|
*/
|
||||||
public function getCMSFields() {
|
public function getCMSFields() {
|
||||||
require_once('Zend/Date.php');
|
require_once('Zend/Date.php');
|
||||||
@ -1535,14 +1535,14 @@ class Member_GroupSet extends ManyManyList {
|
|||||||
* @subpackage security
|
* @subpackage security
|
||||||
*/
|
*/
|
||||||
class Member_ChangePasswordEmail extends Email {
|
class Member_ChangePasswordEmail extends Email {
|
||||||
protected $from = ''; // setting a blank from address uses the site's default administrator email
|
protected $from = ''; // setting a blank from address uses the site's default administrator email
|
||||||
protected $subject = '';
|
protected $subject = '';
|
||||||
protected $ss_template = 'ChangePasswordEmail';
|
protected $ss_template = 'ChangePasswordEmail';
|
||||||
|
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
$this->subject = _t('Member.SUBJECTPASSWORDCHANGED', "Your password has been changed", 'Email subject');
|
$this->subject = _t('Member.SUBJECTPASSWORDCHANGED', "Your password has been changed", 'Email subject');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1553,14 +1553,14 @@ class Member_ChangePasswordEmail extends Email {
|
|||||||
* @subpackage security
|
* @subpackage security
|
||||||
*/
|
*/
|
||||||
class Member_ForgotPasswordEmail extends Email {
|
class Member_ForgotPasswordEmail extends Email {
|
||||||
protected $from = ''; // setting a blank from address uses the site's default administrator email
|
protected $from = ''; // setting a blank from address uses the site's default administrator email
|
||||||
protected $subject = '';
|
protected $subject = '';
|
||||||
protected $ss_template = 'ForgotPasswordEmail';
|
protected $ss_template = 'ForgotPasswordEmail';
|
||||||
|
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
$this->subject = _t('Member.SUBJECTPASSWORDRESET', "Your password reset link", 'Email subject');
|
$this->subject = _t('Member.SUBJECTPASSWORDRESET', "Your password reset link", 'Email subject');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -18,103 +18,103 @@ class MemberAuthenticator extends Authenticator {
|
|||||||
'sha1' => 'sha1_v2.4'
|
'sha1' => 'sha1_v2.4'
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
|
||||||
* Method to authenticate an user
|
|
||||||
*
|
|
||||||
* @param array $RAW_data Raw data to authenticate the user
|
|
||||||
* @param Form $form Optional: If passed, better error messages can be
|
|
||||||
* produced by using
|
|
||||||
* {@link Form::sessionMessage()}
|
|
||||||
* @return bool|Member Returns FALSE if authentication fails, otherwise
|
|
||||||
* the member object
|
|
||||||
* @see Security::setDefaultAdmin()
|
|
||||||
*/
|
|
||||||
public static function authenticate($RAW_data, Form $form = null) {
|
|
||||||
if(array_key_exists('Email', $RAW_data) && $RAW_data['Email']){
|
|
||||||
$SQL_user = Convert::raw2sql($RAW_data['Email']);
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$isLockedOut = false;
|
|
||||||
$result = null;
|
|
||||||
|
|
||||||
// Default login (see Security::setDefaultAdmin())
|
|
||||||
if(Security::check_default_admin($RAW_data['Email'], $RAW_data['Password'])) {
|
|
||||||
$member = Security::findAnAdministrator();
|
|
||||||
} else {
|
|
||||||
$member = DataObject::get_one(
|
|
||||||
"Member",
|
|
||||||
"\"" . Member::get_unique_identifier_field() . "\" = '$SQL_user' AND \"Password\" IS NOT NULL"
|
|
||||||
);
|
|
||||||
|
|
||||||
if($member) {
|
|
||||||
$result = $member->checkPassword($RAW_data['Password']);
|
|
||||||
} else {
|
|
||||||
$result = new ValidationResult(false, _t('Member.ERRORWRONGCRED'));
|
|
||||||
}
|
|
||||||
|
|
||||||
if($member && !$result->valid()) {
|
|
||||||
$member->registerFailedLogin();
|
|
||||||
$member = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Optionally record every login attempt as a {@link LoginAttempt} object
|
|
||||||
/**
|
/**
|
||||||
* TODO We could handle this with an extension
|
* Method to authenticate an user
|
||||||
|
*
|
||||||
|
* @param array $RAW_data Raw data to authenticate the user
|
||||||
|
* @param Form $form Optional: If passed, better error messages can be
|
||||||
|
* produced by using
|
||||||
|
* {@link Form::sessionMessage()}
|
||||||
|
* @return bool|Member Returns FALSE if authentication fails, otherwise
|
||||||
|
* the member object
|
||||||
|
* @see Security::setDefaultAdmin()
|
||||||
*/
|
*/
|
||||||
if(Security::login_recording()) {
|
public static function authenticate($RAW_data, Form $form = null) {
|
||||||
$attempt = new LoginAttempt();
|
if(array_key_exists('Email', $RAW_data) && $RAW_data['Email']){
|
||||||
if($member) {
|
$SQL_user = Convert::raw2sql($RAW_data['Email']);
|
||||||
// successful login (member is existing with matching password)
|
|
||||||
$attempt->MemberID = $member->ID;
|
|
||||||
$attempt->Status = 'Success';
|
|
||||||
|
|
||||||
// Audit logging hook
|
|
||||||
$member->extend('authenticated');
|
|
||||||
} else {
|
} else {
|
||||||
// failed login - we're trying to see if a user exists with this email (disregarding wrong passwords)
|
|
||||||
$existingMember = DataObject::get_one(
|
|
||||||
"Member",
|
|
||||||
"\"" . Member::get_unique_identifier_field() . "\" = '$SQL_user'"
|
|
||||||
);
|
|
||||||
if($existingMember) {
|
|
||||||
$attempt->MemberID = $existingMember->ID;
|
|
||||||
|
|
||||||
// Audit logging hook
|
|
||||||
$existingMember->extend('authenticationFailed');
|
|
||||||
} else {
|
|
||||||
|
|
||||||
// Audit logging hook
|
|
||||||
singleton('Member')->extend('authenticationFailedUnknownUser', $RAW_data);
|
|
||||||
}
|
|
||||||
$attempt->Status = 'Failure';
|
|
||||||
}
|
|
||||||
if(is_array($RAW_data['Email'])) {
|
|
||||||
user_error("Bad email passed to MemberAuthenticator::authenticate(): $RAW_data[Email]", E_USER_WARNING);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$isLockedOut = false;
|
||||||
|
$result = null;
|
||||||
|
|
||||||
|
// Default login (see Security::setDefaultAdmin())
|
||||||
|
if(Security::check_default_admin($RAW_data['Email'], $RAW_data['Password'])) {
|
||||||
|
$member = Security::findAnAdministrator();
|
||||||
|
} else {
|
||||||
|
$member = DataObject::get_one(
|
||||||
|
"Member",
|
||||||
|
"\"" . Member::get_unique_identifier_field() . "\" = '$SQL_user' AND \"Password\" IS NOT NULL"
|
||||||
|
);
|
||||||
|
|
||||||
|
if($member) {
|
||||||
|
$result = $member->checkPassword($RAW_data['Password']);
|
||||||
|
} else {
|
||||||
|
$result = new ValidationResult(false, _t('Member.ERRORWRONGCRED'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if($member && !$result->valid()) {
|
||||||
|
$member->registerFailedLogin();
|
||||||
|
$member = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$attempt->Email = $RAW_data['Email'];
|
// Optionally record every login attempt as a {@link LoginAttempt} object
|
||||||
$attempt->IP = Controller::curr()->getRequest()->getIP();
|
/**
|
||||||
$attempt->write();
|
* TODO We could handle this with an extension
|
||||||
}
|
*/
|
||||||
|
if(Security::login_recording()) {
|
||||||
// Legacy migration to precision-safe password hashes.
|
$attempt = new LoginAttempt();
|
||||||
// A login-event with cleartext passwords is the only time
|
if($member) {
|
||||||
// when we can rehash passwords to a different hashing algorithm,
|
// successful login (member is existing with matching password)
|
||||||
// bulk-migration doesn't work due to the nature of hashing.
|
$attempt->MemberID = $member->ID;
|
||||||
// See PasswordEncryptor_LegacyPHPHash class.
|
$attempt->Status = 'Success';
|
||||||
if(
|
|
||||||
$member // only migrate after successful login
|
// Audit logging hook
|
||||||
&& self::$migrate_legacy_hashes
|
$member->extend('authenticated');
|
||||||
&& array_key_exists($member->PasswordEncryption, self::$migrate_legacy_hashes)
|
} else {
|
||||||
) {
|
// failed login - we're trying to see if a user exists with this email (disregarding wrong passwords)
|
||||||
$member->Password = $RAW_data['Password'];
|
$existingMember = DataObject::get_one(
|
||||||
$member->PasswordEncryption = self::$migrate_legacy_hashes[$member->PasswordEncryption];
|
"Member",
|
||||||
$member->write();
|
"\"" . Member::get_unique_identifier_field() . "\" = '$SQL_user'"
|
||||||
}
|
);
|
||||||
|
if($existingMember) {
|
||||||
|
$attempt->MemberID = $existingMember->ID;
|
||||||
|
|
||||||
|
// Audit logging hook
|
||||||
|
$existingMember->extend('authenticationFailed');
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// Audit logging hook
|
||||||
|
singleton('Member')->extend('authenticationFailedUnknownUser', $RAW_data);
|
||||||
|
}
|
||||||
|
$attempt->Status = 'Failure';
|
||||||
|
}
|
||||||
|
if(is_array($RAW_data['Email'])) {
|
||||||
|
user_error("Bad email passed to MemberAuthenticator::authenticate(): $RAW_data[Email]", E_USER_WARNING);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$attempt->Email = $RAW_data['Email'];
|
||||||
|
$attempt->IP = Controller::curr()->getRequest()->getIP();
|
||||||
|
$attempt->write();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Legacy migration to precision-safe password hashes.
|
||||||
|
// A login-event with cleartext passwords is the only time
|
||||||
|
// when we can rehash passwords to a different hashing algorithm,
|
||||||
|
// bulk-migration doesn't work due to the nature of hashing.
|
||||||
|
// See PasswordEncryptor_LegacyPHPHash class.
|
||||||
|
if(
|
||||||
|
$member // only migrate after successful login
|
||||||
|
&& self::$migrate_legacy_hashes
|
||||||
|
&& array_key_exists($member->PasswordEncryption, self::$migrate_legacy_hashes)
|
||||||
|
) {
|
||||||
|
$member->Password = $RAW_data['Password'];
|
||||||
|
$member->PasswordEncryption = self::$migrate_legacy_hashes[$member->PasswordEncryption];
|
||||||
|
$member->write();
|
||||||
|
}
|
||||||
|
|
||||||
if($member) {
|
if($member) {
|
||||||
Session::clear('BackURL');
|
Session::clear('BackURL');
|
||||||
@ -126,25 +126,25 @@ class MemberAuthenticator extends Authenticator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method that creates the login form for this authentication method
|
* Method that creates the login form for this authentication method
|
||||||
*
|
*
|
||||||
* @param Controller The parent controller, necessary to create the
|
* @param Controller The parent controller, necessary to create the
|
||||||
* appropriate form action tag
|
* appropriate form action tag
|
||||||
* @return Form Returns the login form to use with this authentication
|
* @return Form Returns the login form to use with this authentication
|
||||||
* method
|
* method
|
||||||
*/
|
*/
|
||||||
public static function get_login_form(Controller $controller) {
|
public static function get_login_form(Controller $controller) {
|
||||||
return Object::create("MemberLoginForm", $controller, "LoginForm");
|
return Object::create("MemberLoginForm", $controller, "LoginForm");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the name of the authentication method
|
* Get the name of the authentication method
|
||||||
*
|
*
|
||||||
* @return string Returns the name of the authentication method.
|
* @return string Returns the name of the authentication method.
|
||||||
*/
|
*/
|
||||||
public static function get_name() {
|
public static function get_name() {
|
||||||
return _t('MemberAuthenticator.TITLE', "E-mail & Password");
|
return _t('MemberAuthenticator.TITLE', "E-mail & Password");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ class MemberLoginForm extends LoginForm {
|
|||||||
* @param string $authenticatorClassName Name of the authenticator class that this form uses.
|
* @param string $authenticatorClassName Name of the authenticator class that this form uses.
|
||||||
*/
|
*/
|
||||||
public function __construct($controller, $name, $fields = null, $actions = null,
|
public function __construct($controller, $name, $fields = null, $actions = null,
|
||||||
$checkCurrentUser = true) {
|
$checkCurrentUser = true) {
|
||||||
|
|
||||||
// This is now set on the class directly to make it easier to create subclasses
|
// This is now set on the class directly to make it easier to create subclasses
|
||||||
// $this->authenticator_class = $authenticatorClassName;
|
// $this->authenticator_class = $authenticatorClassName;
|
||||||
@ -226,13 +226,13 @@ JS
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Try to authenticate the user
|
* Try to authenticate the user
|
||||||
*
|
*
|
||||||
* @param array Submitted data
|
* @param array Submitted data
|
||||||
* @return Member Returns the member object on successful authentication
|
* @return Member Returns the member object on successful authentication
|
||||||
* or NULL on failure.
|
* or NULL on failure.
|
||||||
*/
|
*/
|
||||||
public function performLogin($data) {
|
public function performLogin($data) {
|
||||||
$member = call_user_func_array(array($this->authenticator_class, 'authenticate'), array($data, $this));
|
$member = call_user_func_array(array($this->authenticator_class, 'authenticate'), array($data, $this));
|
||||||
if($member) {
|
if($member) {
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
*/
|
*/
|
||||||
class Permission extends DataObject implements TemplateGlobalProvider {
|
class Permission extends DataObject implements TemplateGlobalProvider {
|
||||||
|
|
||||||
// the (1) after Type specifies the DB default value which is needed for
|
// the (1) after Type specifies the DB default value which is needed for
|
||||||
// upgrades from older SilverStripe versions
|
// upgrades from older SilverStripe versions
|
||||||
static $db = array(
|
static $db = array(
|
||||||
"Code" => "Varchar",
|
"Code" => "Varchar",
|
||||||
@ -55,7 +55,7 @@ class Permission extends DataObject implements TemplateGlobalProvider {
|
|||||||
*/
|
*/
|
||||||
static $declared_permissions = null;
|
static $declared_permissions = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Linear list of declared permissions in the system.
|
* Linear list of declared permissions in the system.
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
|
@ -157,7 +157,7 @@ class PermissionCheckboxSetField extends FormField {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$odd = 0;
|
$odd = 0;
|
||||||
$options = '';
|
$options = '';
|
||||||
if($this->source) {
|
if($this->source) {
|
||||||
@ -256,7 +256,7 @@ class PermissionCheckboxSetField extends FormField {
|
|||||||
|
|
||||||
$idList = array();
|
$idList = array();
|
||||||
if($this->value) foreach($this->value as $id => $bool) {
|
if($this->value) foreach($this->value as $id => $bool) {
|
||||||
if($bool) {
|
if($bool) {
|
||||||
$perm = new $managedClass();
|
$perm = new $managedClass();
|
||||||
$perm->{$this->filterField} = $record->ID;
|
$perm->{$this->filterField} = $record->ID;
|
||||||
$perm->Code = $id;
|
$perm->Code = $id;
|
||||||
|
@ -7,18 +7,18 @@
|
|||||||
class Security extends Controller {
|
class Security extends Controller {
|
||||||
|
|
||||||
static $allowed_actions = array(
|
static $allowed_actions = array(
|
||||||
'index',
|
'index',
|
||||||
'login',
|
'login',
|
||||||
'logout',
|
'logout',
|
||||||
'basicauthlogin',
|
'basicauthlogin',
|
||||||
'lostpassword',
|
'lostpassword',
|
||||||
'passwordsent',
|
'passwordsent',
|
||||||
'changepassword',
|
'changepassword',
|
||||||
'ping',
|
'ping',
|
||||||
'LoginForm',
|
'LoginForm',
|
||||||
'ChangePasswordForm',
|
'ChangePasswordForm',
|
||||||
'LostPasswordForm',
|
'LostPasswordForm',
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default user name. Only used in dev-mode by {@link setDefaultAdmin()}
|
* Default user name. Only used in dev-mode by {@link setDefaultAdmin()}
|
||||||
@ -138,22 +138,22 @@ class Security extends Controller {
|
|||||||
* If you don't provide a messageSet, a default will be used.
|
* If you don't provide a messageSet, a default will be used.
|
||||||
*
|
*
|
||||||
* @param Controller $controller The controller that you were on to cause the permission
|
* @param Controller $controller The controller that you were on to cause the permission
|
||||||
* failure.
|
* failure.
|
||||||
* @param string|array $messageSet The message to show to the user. This
|
* @param string|array $messageSet The message to show to the user. This
|
||||||
* can be a string, or a map of different
|
* can be a string, or a map of different
|
||||||
* messages for different contexts.
|
* messages for different contexts.
|
||||||
* If you pass an array, you can use the
|
* If you pass an array, you can use the
|
||||||
* following keys:
|
* following keys:
|
||||||
* - default: The default message
|
* - default: The default message
|
||||||
* - logInAgain: The message to show
|
* - logInAgain: The message to show
|
||||||
* if the user has just
|
* if the user has just
|
||||||
* logged out and the
|
* logged out and the
|
||||||
* - alreadyLoggedIn: The message to
|
* - alreadyLoggedIn: The message to
|
||||||
* show if the user
|
* show if the user
|
||||||
* is already logged
|
* is already logged
|
||||||
* in and lacks the
|
* in and lacks the
|
||||||
* permission to
|
* permission to
|
||||||
* access the item.
|
* access the item.
|
||||||
*
|
*
|
||||||
* The alreadyLoggedIn value can contain a '%s' placeholder that will be replaced with a link
|
* The alreadyLoggedIn value can contain a '%s' placeholder that will be replaced with a link
|
||||||
* to log in.
|
* to log in.
|
||||||
@ -240,7 +240,7 @@ class Security extends Controller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the login form to process according to the submitted data
|
* Get the login form to process according to the submitted data
|
||||||
*/
|
*/
|
||||||
protected function LoginForm() {
|
protected function LoginForm() {
|
||||||
@ -262,7 +262,7 @@ class Security extends Controller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the login forms for all available authentication methods
|
* Get the login forms for all available authentication methods
|
||||||
*
|
*
|
||||||
* @return array Returns an array of available login forms (array of Form
|
* @return array Returns an array of available login forms (array of Form
|
||||||
@ -276,8 +276,8 @@ class Security extends Controller {
|
|||||||
|
|
||||||
$authenticators = Authenticator::get_authenticators();
|
$authenticators = Authenticator::get_authenticators();
|
||||||
foreach($authenticators as $authenticator) {
|
foreach($authenticators as $authenticator) {
|
||||||
array_push($forms,
|
array_push($forms,
|
||||||
call_user_func(array($authenticator, 'get_login_form'),
|
call_user_func(array($authenticator, 'get_login_form'),
|
||||||
$this));
|
$this));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -307,9 +307,9 @@ class Security extends Controller {
|
|||||||
* Log the currently logged in user out
|
* Log the currently logged in user out
|
||||||
*
|
*
|
||||||
* @param bool $redirect Redirect the user back to where they came.
|
* @param bool $redirect Redirect the user back to where they came.
|
||||||
* - If it's false, the code calling logout() is
|
* - If it's false, the code calling logout() is
|
||||||
* responsible for sending the user where-ever
|
* responsible for sending the user where-ever
|
||||||
* they should go.
|
* they should go.
|
||||||
*/
|
*/
|
||||||
public function logout($redirect = true) {
|
public function logout($redirect = true) {
|
||||||
$member = Member::currentUser();
|
$member = Member::currentUser();
|
||||||
@ -675,7 +675,7 @@ class Security extends Controller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($adminGroup) {
|
if ($adminGroup) {
|
||||||
$member = $adminGroup->Members()->First();
|
$member = $adminGroup->Members()->First();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!$adminGroup) {
|
if(!$adminGroup) {
|
||||||
|
@ -37,7 +37,7 @@ class RestfulServiceTest extends SapphireTest {
|
|||||||
$service->setQueryString($params);
|
$service->setQueryString($params);
|
||||||
$responseBody = $service->request($url)->getBody();
|
$responseBody = $service->request($url)->getBody();
|
||||||
foreach ($params as $key => $value) {
|
foreach ($params as $key => $value) {
|
||||||
$this->assertContains("<request_item name=\"$key\">$value</request_item>", $responseBody);
|
$this->assertContains("<request_item name=\"$key\">$value</request_item>", $responseBody);
|
||||||
$this->assertContains("<get_item name=\"$key\">$value</get_item>", $responseBody);
|
$this->assertContains("<get_item name=\"$key\">$value</get_item>", $responseBody);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -52,7 +52,7 @@ class RestfulServiceTest extends SapphireTest {
|
|||||||
$service->setQueryString($params);
|
$service->setQueryString($params);
|
||||||
$responseBody = $service->request($url)->getBody();
|
$responseBody = $service->request($url)->getBody();
|
||||||
foreach ($params as $key => $value) {
|
foreach ($params as $key => $value) {
|
||||||
$this->assertContains("<request_item name=\"$key\">$value</request_item>", $responseBody);
|
$this->assertContains("<request_item name=\"$key\">$value</request_item>", $responseBody);
|
||||||
$this->assertContains("<get_item name=\"$key\">$value</get_item>", $responseBody);
|
$this->assertContains("<get_item name=\"$key\">$value</get_item>", $responseBody);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -67,7 +67,7 @@ class RestfulServiceTest extends SapphireTest {
|
|||||||
$url .= '?' . http_build_query($params);
|
$url .= '?' . http_build_query($params);
|
||||||
$responseBody = $service->request($url)->getBody();
|
$responseBody = $service->request($url)->getBody();
|
||||||
foreach ($params as $key => $value) {
|
foreach ($params as $key => $value) {
|
||||||
$this->assertContains("<request_item name=\"$key\">$value</request_item>", $responseBody);
|
$this->assertContains("<request_item name=\"$key\">$value</request_item>", $responseBody);
|
||||||
$this->assertContains("<get_item name=\"$key\">$value</get_item>", $responseBody);
|
$this->assertContains("<get_item name=\"$key\">$value</get_item>", $responseBody);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,10 +3,10 @@
|
|||||||
namespace SilverStripe\Framework\Test\Behaviour;
|
namespace SilverStripe\Framework\Test\Behaviour;
|
||||||
|
|
||||||
use SilverStripe\BehatExtension\Context\SilverStripeContext,
|
use SilverStripe\BehatExtension\Context\SilverStripeContext,
|
||||||
SilverStripe\BehatExtension\Context\BasicContext,
|
SilverStripe\BehatExtension\Context\BasicContext,
|
||||||
SilverStripe\BehatExtension\Context\LoginContext,
|
SilverStripe\BehatExtension\Context\LoginContext,
|
||||||
SilverStripe\Framework\Test\Behaviour\CmsFormsContext,
|
SilverStripe\Framework\Test\Behaviour\CmsFormsContext,
|
||||||
SilverStripe\Framework\Test\Behaviour\CmsUiContext;
|
SilverStripe\Framework\Test\Behaviour\CmsUiContext;
|
||||||
|
|
||||||
// PHPUnit
|
// PHPUnit
|
||||||
require_once 'PHPUnit/Autoload.php';
|
require_once 'PHPUnit/Autoload.php';
|
||||||
@ -20,19 +20,19 @@ require_once 'PHPUnit/Framework/Assert/Functions.php';
|
|||||||
*/
|
*/
|
||||||
class FeatureContext extends SilverStripeContext
|
class FeatureContext extends SilverStripeContext
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Initializes context.
|
* Initializes context.
|
||||||
* Every scenario gets it's own context object.
|
* Every scenario gets it's own context object.
|
||||||
*
|
*
|
||||||
* @param array $parameters context parameters (set them up through behat.yml)
|
* @param array $parameters context parameters (set them up through behat.yml)
|
||||||
*/
|
*/
|
||||||
public function __construct(array $parameters)
|
public function __construct(array $parameters)
|
||||||
{
|
{
|
||||||
$this->useContext('BasicContext', new BasicContext($parameters));
|
$this->useContext('BasicContext', new BasicContext($parameters));
|
||||||
$this->useContext('LoginContext', new LoginContext($parameters));
|
$this->useContext('LoginContext', new LoginContext($parameters));
|
||||||
$this->useContext('CmsFormsContext', new CmsFormsContext($parameters));
|
$this->useContext('CmsFormsContext', new CmsFormsContext($parameters));
|
||||||
$this->useContext('CmsUiContext', new CmsUiContext($parameters));
|
$this->useContext('CmsUiContext', new CmsUiContext($parameters));
|
||||||
|
|
||||||
parent::__construct($parameters);
|
parent::__construct($parameters);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,12 +3,12 @@
|
|||||||
namespace SilverStripe\Framework\Test\Behaviour;
|
namespace SilverStripe\Framework\Test\Behaviour;
|
||||||
|
|
||||||
use Behat\Behat\Context\ClosuredContextInterface,
|
use Behat\Behat\Context\ClosuredContextInterface,
|
||||||
Behat\Behat\Context\TranslatedContextInterface,
|
Behat\Behat\Context\TranslatedContextInterface,
|
||||||
Behat\Behat\Context\BehatContext,
|
Behat\Behat\Context\BehatContext,
|
||||||
Behat\Behat\Context\Step,
|
Behat\Behat\Context\Step,
|
||||||
Behat\Behat\Exception\PendingException;
|
Behat\Behat\Exception\PendingException;
|
||||||
use Behat\Gherkin\Node\PyStringNode,
|
use Behat\Gherkin\Node\PyStringNode,
|
||||||
Behat\Gherkin\Node\TableNode;
|
Behat\Gherkin\Node\TableNode;
|
||||||
|
|
||||||
// PHPUnit
|
// PHPUnit
|
||||||
require_once 'PHPUnit/Autoload.php';
|
require_once 'PHPUnit/Autoload.php';
|
||||||
@ -21,81 +21,81 @@ require_once 'PHPUnit/Framework/Assert/Functions.php';
|
|||||||
*/
|
*/
|
||||||
class CmsFormsContext extends BehatContext
|
class CmsFormsContext extends BehatContext
|
||||||
{
|
{
|
||||||
protected $context;
|
protected $context;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes context.
|
* Initializes context.
|
||||||
* Every scenario gets it's own context object.
|
* Every scenario gets it's own context object.
|
||||||
*
|
*
|
||||||
* @param array $parameters context parameters (set them up through behat.yml)
|
* @param array $parameters context parameters (set them up through behat.yml)
|
||||||
*/
|
*/
|
||||||
public function __construct(array $parameters)
|
public function __construct(array $parameters)
|
||||||
{
|
{
|
||||||
// Initialize your context here
|
// Initialize your context here
|
||||||
$this->context = $parameters;
|
$this->context = $parameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get Mink session from MinkContext
|
* Get Mink session from MinkContext
|
||||||
*/
|
*/
|
||||||
public function getSession($name = null)
|
public function getSession($name = null)
|
||||||
{
|
{
|
||||||
return $this->getMainContext()->getSession($name);
|
return $this->getMainContext()->getSession($name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Then /^I should see an edit page form$/
|
* @Then /^I should see an edit page form$/
|
||||||
*/
|
*/
|
||||||
public function stepIShouldSeeAnEditPageForm()
|
public function stepIShouldSeeAnEditPageForm()
|
||||||
{
|
{
|
||||||
$page = $this->getSession()->getPage();
|
$page = $this->getSession()->getPage();
|
||||||
|
|
||||||
$form = $page->find('css', '#Form_EditForm');
|
$form = $page->find('css', '#Form_EditForm');
|
||||||
assertNotNull($form, 'I should see an edit page form');
|
assertNotNull($form, 'I should see an edit page form');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @When /^I fill in the "(?P<field>([^"]*))" HTML field with "(?P<value>([^"]*))"$/
|
* @When /^I fill in the "(?P<field>([^"]*))" HTML field with "(?P<value>([^"]*))"$/
|
||||||
* @When /^I fill in "(?P<value>([^"]*))" for the "(?P<field>([^"]*))" HTML field$/
|
* @When /^I fill in "(?P<value>([^"]*))" for the "(?P<field>([^"]*))" HTML field$/
|
||||||
*/
|
*/
|
||||||
public function stepIFillInTheHtmlFieldWith($field, $value)
|
public function stepIFillInTheHtmlFieldWith($field, $value)
|
||||||
{
|
{
|
||||||
$page = $this->getSession()->getPage();
|
$page = $this->getSession()->getPage();
|
||||||
$inputField = $page->findField($field);
|
$inputField = $page->findField($field);
|
||||||
assertNotNull($inputField, sprintf('HTML field "%s" not found', $field));
|
assertNotNull($inputField, sprintf('HTML field "%s" not found', $field));
|
||||||
|
|
||||||
$this->getSession()->evaluateScript(sprintf(
|
$this->getSession()->evaluateScript(sprintf(
|
||||||
"jQuery('#%s').entwine('ss').getEditor().setContent('%s')",
|
"jQuery('#%s').entwine('ss').getEditor().setContent('%s')",
|
||||||
$inputField->getAttribute('id'),
|
$inputField->getAttribute('id'),
|
||||||
addcslashes($value, "'")
|
addcslashes($value, "'")
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @When /^I append "(?P<value>([^"]*))" to the "(?P<field>([^"]*))" HTML field$/
|
* @When /^I append "(?P<value>([^"]*))" to the "(?P<field>([^"]*))" HTML field$/
|
||||||
*/
|
*/
|
||||||
public function stepIAppendTotheHtmlField($field, $value)
|
public function stepIAppendTotheHtmlField($field, $value)
|
||||||
{
|
{
|
||||||
$page = $this->getSession()->getPage();
|
$page = $this->getSession()->getPage();
|
||||||
$inputField = $page->findField($field);
|
$inputField = $page->findField($field);
|
||||||
assertNotNull($inputField, sprintf('HTML field "%s" not found', $field));
|
assertNotNull($inputField, sprintf('HTML field "%s" not found', $field));
|
||||||
|
|
||||||
$this->getSession()->evaluateScript(sprintf(
|
$this->getSession()->evaluateScript(sprintf(
|
||||||
"jQuery('#%s').entwine('ss').getEditor().insertContent('%s')",
|
"jQuery('#%s').entwine('ss').getEditor().insertContent('%s')",
|
||||||
$inputField->getAttribute('id'),
|
$inputField->getAttribute('id'),
|
||||||
addcslashes($value, "'")
|
addcslashes($value, "'")
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Then /^the "(?P<field>([^"]*))" HTML field should contain "(?P<value>([^"]*))"$/
|
* @Then /^the "(?P<field>([^"]*))" HTML field should contain "(?P<value>([^"]*))"$/
|
||||||
*/
|
*/
|
||||||
public function theHtmlFieldShouldContain($field, $value)
|
public function theHtmlFieldShouldContain($field, $value)
|
||||||
{
|
{
|
||||||
$page = $this->getSession()->getPage();
|
$page = $this->getSession()->getPage();
|
||||||
$inputField = $page->findField($field);
|
$inputField = $page->findField($field);
|
||||||
assertNotNull($inputField, sprintf('HTML field "%s" not found', $field));
|
assertNotNull($inputField, sprintf('HTML field "%s" not found', $field));
|
||||||
|
|
||||||
$this->getMainContext()->assertElementContains('#' . $inputField->getAttribute('id'), $value);
|
$this->getMainContext()->assertElementContains('#' . $inputField->getAttribute('id'), $value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,13 +3,13 @@
|
|||||||
namespace SilverStripe\Framework\Test\Behaviour;
|
namespace SilverStripe\Framework\Test\Behaviour;
|
||||||
|
|
||||||
use Behat\Behat\Context\ClosuredContextInterface,
|
use Behat\Behat\Context\ClosuredContextInterface,
|
||||||
Behat\Behat\Context\TranslatedContextInterface,
|
Behat\Behat\Context\TranslatedContextInterface,
|
||||||
Behat\Behat\Context\BehatContext,
|
Behat\Behat\Context\BehatContext,
|
||||||
Behat\Behat\Context\Step,
|
Behat\Behat\Context\Step,
|
||||||
Behat\Behat\Exception\PendingException,
|
Behat\Behat\Exception\PendingException,
|
||||||
Behat\Mink\Exception\ElementNotFoundException;
|
Behat\Mink\Exception\ElementNotFoundException;
|
||||||
use Behat\Gherkin\Node\PyStringNode,
|
use Behat\Gherkin\Node\PyStringNode,
|
||||||
Behat\Gherkin\Node\TableNode;
|
Behat\Gherkin\Node\TableNode;
|
||||||
|
|
||||||
|
|
||||||
// PHPUnit
|
// PHPUnit
|
||||||
@ -23,376 +23,376 @@ require_once 'PHPUnit/Framework/Assert/Functions.php';
|
|||||||
*/
|
*/
|
||||||
class CmsUiContext extends BehatContext
|
class CmsUiContext extends BehatContext
|
||||||
{
|
{
|
||||||
protected $context;
|
protected $context;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes context.
|
* Initializes context.
|
||||||
* Every scenario gets it's own context object.
|
* Every scenario gets it's own context object.
|
||||||
*
|
*
|
||||||
* @param array $parameters context parameters (set them up through behat.yml)
|
* @param array $parameters context parameters (set them up through behat.yml)
|
||||||
*/
|
*/
|
||||||
public function __construct(array $parameters)
|
public function __construct(array $parameters)
|
||||||
{
|
{
|
||||||
// Initialize your context here
|
// Initialize your context here
|
||||||
$this->context = $parameters;
|
$this->context = $parameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get Mink session from MinkContext
|
* Get Mink session from MinkContext
|
||||||
*/
|
*/
|
||||||
public function getSession($name = null)
|
public function getSession($name = null)
|
||||||
{
|
{
|
||||||
return $this->getMainContext()->getSession($name);
|
return $this->getMainContext()->getSession($name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Then /^I should see the CMS$/
|
* @Then /^I should see the CMS$/
|
||||||
*/
|
*/
|
||||||
public function iShouldSeeTheCms()
|
public function iShouldSeeTheCms()
|
||||||
{
|
{
|
||||||
$page = $this->getSession()->getPage();
|
$page = $this->getSession()->getPage();
|
||||||
$cms_element = $page->find('css', '.cms');
|
$cms_element = $page->find('css', '.cms');
|
||||||
assertNotNull($cms_element, 'CMS not found');
|
assertNotNull($cms_element, 'CMS not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Then /^I should see a "([^"]*)" notice$/
|
* @Then /^I should see a "([^"]*)" notice$/
|
||||||
*/
|
*/
|
||||||
public function iShouldSeeANotice($notice)
|
public function iShouldSeeANotice($notice)
|
||||||
{
|
{
|
||||||
$this->getMainContext()->assertElementContains('.notice-wrap', $notice);
|
$this->getMainContext()->assertElementContains('.notice-wrap', $notice);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Then /^I should see a "([^"]*)" message$/
|
* @Then /^I should see a "([^"]*)" message$/
|
||||||
*/
|
*/
|
||||||
public function iShouldSeeAMessage($message)
|
public function iShouldSeeAMessage($message)
|
||||||
{
|
{
|
||||||
$this->getMainContext()->assertElementContains('.message', $message);
|
$this->getMainContext()->assertElementContains('.message', $message);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getCmsTabsElement()
|
protected function getCmsTabsElement()
|
||||||
{
|
{
|
||||||
$this->getSession()->wait(5000, "window.jQuery('.cms-content-header-tabs').size() > 0");
|
$this->getSession()->wait(5000, "window.jQuery('.cms-content-header-tabs').size() > 0");
|
||||||
|
|
||||||
$page = $this->getSession()->getPage();
|
$page = $this->getSession()->getPage();
|
||||||
$cms_content_header_tabs = $page->find('css', '.cms-content-header-tabs');
|
$cms_content_header_tabs = $page->find('css', '.cms-content-header-tabs');
|
||||||
assertNotNull($cms_content_header_tabs, 'CMS tabs not found');
|
assertNotNull($cms_content_header_tabs, 'CMS tabs not found');
|
||||||
|
|
||||||
return $cms_content_header_tabs;
|
return $cms_content_header_tabs;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getCmsContentToolbarElement()
|
protected function getCmsContentToolbarElement()
|
||||||
{
|
{
|
||||||
$this->getSession()->wait(
|
$this->getSession()->wait(
|
||||||
5000,
|
5000,
|
||||||
"window.jQuery('.cms-content-toolbar').size() > 0 "
|
"window.jQuery('.cms-content-toolbar').size() > 0 "
|
||||||
. "&& window.jQuery('.cms-content-toolbar').children().size() > 0"
|
. "&& window.jQuery('.cms-content-toolbar').children().size() > 0"
|
||||||
);
|
);
|
||||||
|
|
||||||
$page = $this->getSession()->getPage();
|
$page = $this->getSession()->getPage();
|
||||||
$cms_content_toolbar_element = $page->find('css', '.cms-content-toolbar');
|
$cms_content_toolbar_element = $page->find('css', '.cms-content-toolbar');
|
||||||
assertNotNull($cms_content_toolbar_element, 'CMS content toolbar not found');
|
assertNotNull($cms_content_toolbar_element, 'CMS content toolbar not found');
|
||||||
|
|
||||||
return $cms_content_toolbar_element;
|
return $cms_content_toolbar_element;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getCmsTreeElement()
|
protected function getCmsTreeElement()
|
||||||
{
|
{
|
||||||
$this->getSession()->wait(5000, "window.jQuery('.cms-tree').size() > 0");
|
$this->getSession()->wait(5000, "window.jQuery('.cms-tree').size() > 0");
|
||||||
|
|
||||||
$page = $this->getSession()->getPage();
|
$page = $this->getSession()->getPage();
|
||||||
$cms_tree_element = $page->find('css', '.cms-tree');
|
$cms_tree_element = $page->find('css', '.cms-tree');
|
||||||
assertNotNull($cms_tree_element, 'CMS tree not found');
|
assertNotNull($cms_tree_element, 'CMS tree not found');
|
||||||
|
|
||||||
return $cms_tree_element;
|
return $cms_tree_element;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getGridfieldTable($title)
|
protected function getGridfieldTable($title)
|
||||||
{
|
{
|
||||||
$page = $this->getSession()->getPage();
|
$page = $this->getSession()->getPage();
|
||||||
$table_elements = $page->findAll('css', '.ss-gridfield-table');
|
$table_elements = $page->findAll('css', '.ss-gridfield-table');
|
||||||
assertNotNull($table_elements, 'Table elements not found');
|
assertNotNull($table_elements, 'Table elements not found');
|
||||||
|
|
||||||
$table_element = null;
|
$table_element = null;
|
||||||
foreach ($table_elements as $table) {
|
foreach ($table_elements as $table) {
|
||||||
$table_title_element = $table->find('css', '.title');
|
$table_title_element = $table->find('css', '.title');
|
||||||
if ($table_title_element->getText() === $title) {
|
if ($table_title_element->getText() === $title) {
|
||||||
$table_element = $table;
|
$table_element = $table;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assertNotNull($table_element, sprintf('Table `%s` not found', $title));
|
assertNotNull($table_element, sprintf('Table `%s` not found', $title));
|
||||||
|
|
||||||
return $table_element;
|
return $table_element;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Given /^I should see a "([^"]*)" button in CMS Content Toolbar$/
|
* @Given /^I should see a "([^"]*)" button in CMS Content Toolbar$/
|
||||||
*/
|
*/
|
||||||
public function iShouldSeeAButtonInCmsContentToolbar($text)
|
public function iShouldSeeAButtonInCmsContentToolbar($text)
|
||||||
{
|
{
|
||||||
$cms_content_toolbar_element = $this->getCmsContentToolbarElement();
|
$cms_content_toolbar_element = $this->getCmsContentToolbarElement();
|
||||||
|
|
||||||
$element = $cms_content_toolbar_element->find('named', array('link_or_button', "'$text'"));
|
$element = $cms_content_toolbar_element->find('named', array('link_or_button', "'$text'"));
|
||||||
assertNotNull($element, sprintf('%s button not found', $text));
|
assertNotNull($element, sprintf('%s button not found', $text));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @When /^I should see "([^"]*)" in CMS Tree$/
|
* @When /^I should see "([^"]*)" in CMS Tree$/
|
||||||
*/
|
*/
|
||||||
public function stepIShouldSeeInCmsTree($text)
|
public function stepIShouldSeeInCmsTree($text)
|
||||||
{
|
{
|
||||||
$cms_tree_element = $this->getCmsTreeElement();
|
$cms_tree_element = $this->getCmsTreeElement();
|
||||||
|
|
||||||
$element = $cms_tree_element->find('named', array('content', "'$text'"));
|
$element = $cms_tree_element->find('named', array('content', "'$text'"));
|
||||||
assertNotNull($element, sprintf('%s not found', $text));
|
assertNotNull($element, sprintf('%s not found', $text));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @When /^I should not see "([^"]*)" in CMS Tree$/
|
* @When /^I should not see "([^"]*)" in CMS Tree$/
|
||||||
*/
|
*/
|
||||||
public function stepIShouldNotSeeInCmsTree($text)
|
public function stepIShouldNotSeeInCmsTree($text)
|
||||||
{
|
{
|
||||||
$cms_tree_element = $this->getCmsTreeElement();
|
$cms_tree_element = $this->getCmsTreeElement();
|
||||||
|
|
||||||
$element = $cms_tree_element->find('named', array('content', "'$text'"));
|
$element = $cms_tree_element->find('named', array('content', "'$text'"));
|
||||||
assertNull($element, sprintf('%s found', $text));
|
assertNull($element, sprintf('%s found', $text));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @When /^I expand the "([^"]*)" CMS Panel$/
|
* @When /^I expand the "([^"]*)" CMS Panel$/
|
||||||
*/
|
*/
|
||||||
public function iExpandTheCmsPanel()
|
public function iExpandTheCmsPanel()
|
||||||
{
|
{
|
||||||
// TODO Make dynamic, currently hardcoded to first panel
|
// TODO Make dynamic, currently hardcoded to first panel
|
||||||
$page = $this->getSession()->getPage();
|
$page = $this->getSession()->getPage();
|
||||||
|
|
||||||
$panel_toggle_element = $page->find('css', '.cms-content > .cms-panel > .cms-panel-toggle > .toggle-expand');
|
$panel_toggle_element = $page->find('css', '.cms-content > .cms-panel > .cms-panel-toggle > .toggle-expand');
|
||||||
assertNotNull($panel_toggle_element, 'Panel toggle not found');
|
assertNotNull($panel_toggle_element, 'Panel toggle not found');
|
||||||
|
|
||||||
if ($panel_toggle_element->isVisible()) {
|
if ($panel_toggle_element->isVisible()) {
|
||||||
$panel_toggle_element->click();
|
$panel_toggle_element->click();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @When /^I click the "([^"]*)" CMS tab$/
|
* @When /^I click the "([^"]*)" CMS tab$/
|
||||||
*/
|
*/
|
||||||
public function iClickTheCmsTab($tab)
|
public function iClickTheCmsTab($tab)
|
||||||
{
|
{
|
||||||
$this->getSession()->wait(5000, "window.jQuery('.ui-tabs-nav').size() > 0");
|
$this->getSession()->wait(5000, "window.jQuery('.ui-tabs-nav').size() > 0");
|
||||||
|
|
||||||
$page = $this->getSession()->getPage();
|
$page = $this->getSession()->getPage();
|
||||||
$tabsets = $page->findAll('css', '.ui-tabs-nav');
|
$tabsets = $page->findAll('css', '.ui-tabs-nav');
|
||||||
assertNotNull($tabsets, 'CMS tabs not found');
|
assertNotNull($tabsets, 'CMS tabs not found');
|
||||||
|
|
||||||
$tab_element = null;
|
$tab_element = null;
|
||||||
foreach($tabsets as $tabset) {
|
foreach($tabsets as $tabset) {
|
||||||
if($tab_element) continue;
|
if($tab_element) continue;
|
||||||
$tab_element = $tabset->find('named', array('link_or_button', "'$tab'"));
|
$tab_element = $tabset->find('named', array('link_or_button', "'$tab'"));
|
||||||
}
|
}
|
||||||
assertNotNull($tab_element, sprintf('%s tab not found', $tab));
|
assertNotNull($tab_element, sprintf('%s tab not found', $tab));
|
||||||
|
|
||||||
$tab_element->click();
|
$tab_element->click();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Then /^the "([^"]*)" table should contain "([^"]*)"$/
|
* @Then /^the "([^"]*)" table should contain "([^"]*)"$/
|
||||||
*/
|
*/
|
||||||
public function theTableShouldContain($table, $text)
|
public function theTableShouldContain($table, $text)
|
||||||
{
|
{
|
||||||
$table_element = $this->getGridfieldTable($table);
|
$table_element = $this->getGridfieldTable($table);
|
||||||
|
|
||||||
$element = $table_element->find('named', array('content', "'$text'"));
|
$element = $table_element->find('named', array('content', "'$text'"));
|
||||||
assertNotNull($element, sprintf('Element containing `%s` not found in `%s` table', $text, $table));
|
assertNotNull($element, sprintf('Element containing `%s` not found in `%s` table', $text, $table));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Then /^the "([^"]*)" table should not contain "([^"]*)"$/
|
* @Then /^the "([^"]*)" table should not contain "([^"]*)"$/
|
||||||
*/
|
*/
|
||||||
public function theTableShouldNotContain($table, $text)
|
public function theTableShouldNotContain($table, $text)
|
||||||
{
|
{
|
||||||
$table_element = $this->getGridfieldTable($table);
|
$table_element = $this->getGridfieldTable($table);
|
||||||
|
|
||||||
$element = $table_element->find('named', array('content', "'$text'"));
|
$element = $table_element->find('named', array('content', "'$text'"));
|
||||||
assertNull($element, sprintf('Element containing `%s` not found in `%s` table', $text, $table));
|
assertNull($element, sprintf('Element containing `%s` not found in `%s` table', $text, $table));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Given /^I click on "([^"]*)" in the "([^"]*)" table$/
|
* @Given /^I click on "([^"]*)" in the "([^"]*)" table$/
|
||||||
*/
|
*/
|
||||||
public function iClickOnInTheTable($text, $table)
|
public function iClickOnInTheTable($text, $table)
|
||||||
{
|
{
|
||||||
$table_element = $this->getGridfieldTable($table);
|
$table_element = $this->getGridfieldTable($table);
|
||||||
|
|
||||||
$element = $table_element->find('xpath', sprintf('//*[count(*)=0 and contains(.,"%s")]', $text));
|
$element = $table_element->find('xpath', sprintf('//*[count(*)=0 and contains(.,"%s")]', $text));
|
||||||
assertNotNull($element, sprintf('Element containing `%s` not found', $text));
|
assertNotNull($element, sprintf('Element containing `%s` not found', $text));
|
||||||
$element->click();
|
$element->click();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Then /^I can see the preview panel$/
|
* @Then /^I can see the preview panel$/
|
||||||
*/
|
*/
|
||||||
public function iCanSeeThePreviewPanel()
|
public function iCanSeeThePreviewPanel()
|
||||||
{
|
{
|
||||||
$this->getMainContext()->assertElementOnPage('.cms-preview');
|
$this->getMainContext()->assertElementOnPage('.cms-preview');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Given /^the preview contains "([^"]*)"$/
|
* @Given /^the preview contains "([^"]*)"$/
|
||||||
*/
|
*/
|
||||||
public function thePreviewContains($content)
|
public function thePreviewContains($content)
|
||||||
{
|
{
|
||||||
$driver = $this->getSession()->getDriver();
|
$driver = $this->getSession()->getDriver();
|
||||||
$driver->switchToIFrame('cms-preview-iframe');
|
$driver->switchToIFrame('cms-preview-iframe');
|
||||||
|
|
||||||
$this->getMainContext()->assertPageContainsText($content);
|
$this->getMainContext()->assertPageContainsText($content);
|
||||||
$driver->switchToWindow();
|
$driver->switchToWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Given /^I set the CMS mode to "([^"]*)"$/
|
* @Given /^I set the CMS mode to "([^"]*)"$/
|
||||||
*/
|
*/
|
||||||
public function iSetTheCmsToMode($mode)
|
public function iSetTheCmsToMode($mode)
|
||||||
{
|
{
|
||||||
return array(
|
return array(
|
||||||
new Step\When(sprintf('I fill in the "Change view mode" dropdown with "%s"', $mode)),
|
new Step\When(sprintf('I fill in the "Change view mode" dropdown with "%s"', $mode)),
|
||||||
new Step\When('I wait for 1 second') // wait for CMS layout to redraw
|
new Step\When('I wait for 1 second') // wait for CMS layout to redraw
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Given /^I wait for the preview to load$/
|
* @Given /^I wait for the preview to load$/
|
||||||
*/
|
*/
|
||||||
public function iWaitForThePreviewToLoad()
|
public function iWaitForThePreviewToLoad()
|
||||||
{
|
{
|
||||||
$driver = $this->getSession()->getDriver();
|
$driver = $this->getSession()->getDriver();
|
||||||
$driver->switchToIFrame('cms-preview-iframe');
|
$driver->switchToIFrame('cms-preview-iframe');
|
||||||
|
|
||||||
$this->getSession()->wait(
|
$this->getSession()->wait(
|
||||||
5000,
|
5000,
|
||||||
"!jQuery('iframe[name=cms-preview-iframe]').hasClass('loading')"
|
"!jQuery('iframe[name=cms-preview-iframe]').hasClass('loading')"
|
||||||
);
|
);
|
||||||
|
|
||||||
$driver->switchToWindow();
|
$driver->switchToWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Given /^I switch the preview to "([^"]*)"$/
|
* @Given /^I switch the preview to "([^"]*)"$/
|
||||||
*/
|
*/
|
||||||
public function iSwitchThePreviewToMode($mode)
|
public function iSwitchThePreviewToMode($mode)
|
||||||
{
|
{
|
||||||
$controls = $this->getSession()->getPage()->find('css', '.cms-preview-controls');
|
$controls = $this->getSession()->getPage()->find('css', '.cms-preview-controls');
|
||||||
assertNotNull($controls, 'Preview controls not found');
|
assertNotNull($controls, 'Preview controls not found');
|
||||||
|
|
||||||
$label = $controls->find('xpath', sprintf(
|
$label = $controls->find('xpath', sprintf(
|
||||||
'.//label[(@for="%s")]',
|
'.//label[(@for="%s")]',
|
||||||
$mode
|
$mode
|
||||||
));
|
));
|
||||||
assertNotNull($label, 'Preview mode switch not found');
|
assertNotNull($label, 'Preview mode switch not found');
|
||||||
|
|
||||||
$label->click();
|
$label->click();
|
||||||
|
|
||||||
return new Step\When('I wait for the preview to load');
|
return new Step\When('I wait for the preview to load');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Given /^the preview does not contain "([^"]*)"$/
|
* @Given /^the preview does not contain "([^"]*)"$/
|
||||||
*/
|
*/
|
||||||
public function thePreviewDoesNotContain($content)
|
public function thePreviewDoesNotContain($content)
|
||||||
{
|
{
|
||||||
$driver = $this->getSession()->getDriver();
|
$driver = $this->getSession()->getDriver();
|
||||||
$driver->switchToIFrame('cms-preview-iframe');
|
$driver->switchToIFrame('cms-preview-iframe');
|
||||||
|
|
||||||
$this->getMainContext()->assertPageNotContainsText($content);
|
$this->getMainContext()->assertPageNotContainsText($content);
|
||||||
$driver->switchToWindow();
|
$driver->switchToWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Workaround for chosen.js dropdowns which hide the original dropdown field.
|
* Workaround for chosen.js dropdowns which hide the original dropdown field.
|
||||||
*
|
*
|
||||||
* @When /^(?:|I )fill in the "(?P<field>(?:[^"]|\\")*)" dropdown with "(?P<value>(?:[^"]|\\")*)"$/
|
* @When /^(?:|I )fill in the "(?P<field>(?:[^"]|\\")*)" dropdown with "(?P<value>(?:[^"]|\\")*)"$/
|
||||||
* @When /^(?:|I )fill in "(?P<value>(?:[^"]|\\")*)" for the "(?P<field>(?:[^"]|\\")*)" dropdown$/
|
* @When /^(?:|I )fill in "(?P<value>(?:[^"]|\\")*)" for the "(?P<field>(?:[^"]|\\")*)" dropdown$/
|
||||||
*/
|
*/
|
||||||
public function theIFillInTheDropdownWith($field, $value)
|
public function theIFillInTheDropdownWith($field, $value)
|
||||||
{
|
{
|
||||||
$field = $this->fixStepArgument($field);
|
$field = $this->fixStepArgument($field);
|
||||||
$value = $this->fixStepArgument($value);
|
$value = $this->fixStepArgument($value);
|
||||||
|
|
||||||
// Given the fuzzy matching, we might get more than one matching field.
|
// Given the fuzzy matching, we might get more than one matching field.
|
||||||
$formFields = array();
|
$formFields = array();
|
||||||
|
|
||||||
// Find by label
|
// Find by label
|
||||||
$formField = $this->getSession()->getPage()->findField($field);
|
$formField = $this->getSession()->getPage()->findField($field);
|
||||||
if($formField) $formFields[] = $formField;
|
if($formField) $formFields[] = $formField;
|
||||||
|
|
||||||
// Fall back to finding by title (for dropdowns without a label)
|
// Fall back to finding by title (for dropdowns without a label)
|
||||||
if(!$formFields) {
|
if(!$formFields) {
|
||||||
$formFields = $this->getSession()->getPage()->findAll(
|
$formFields = $this->getSession()->getPage()->findAll(
|
||||||
'xpath',
|
'xpath',
|
||||||
sprintf(
|
sprintf(
|
||||||
'//*[self::select][(./@title="%s")]',
|
'//*[self::select][(./@title="%s")]',
|
||||||
$field
|
$field
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
assertGreaterThan(0, count($formFields), sprintf(
|
assertGreaterThan(0, count($formFields), sprintf(
|
||||||
'Chosen.js dropdown named "%s" not found',
|
'Chosen.js dropdown named "%s" not found',
|
||||||
$field
|
$field
|
||||||
));
|
));
|
||||||
|
|
||||||
$containers = array();
|
$containers = array();
|
||||||
foreach($formFields as $formField) {
|
foreach($formFields as $formField) {
|
||||||
// Traverse up to field holder
|
// Traverse up to field holder
|
||||||
$containerCandidate = $formField;
|
$containerCandidate = $formField;
|
||||||
do {
|
do {
|
||||||
$containerCandidate = $containerCandidate->getParent();
|
$containerCandidate = $containerCandidate->getParent();
|
||||||
} while($containerCandidate && !preg_match('/field/', $containerCandidate->getAttribute('class')));
|
} while($containerCandidate && !preg_match('/field/', $containerCandidate->getAttribute('class')));
|
||||||
|
|
||||||
if(
|
if(
|
||||||
$containerCandidate
|
$containerCandidate
|
||||||
&& $containerCandidate->isVisible()
|
&& $containerCandidate->isVisible()
|
||||||
&& preg_match('/field/', $containerCandidate->getAttribute('class'))
|
&& preg_match('/field/', $containerCandidate->getAttribute('class'))
|
||||||
) {
|
) {
|
||||||
$containers[] = $containerCandidate;
|
$containers[] = $containerCandidate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
assertGreaterThan(0, count($containers), 'Chosen.js field container not found');
|
assertGreaterThan(0, count($containers), 'Chosen.js field container not found');
|
||||||
|
|
||||||
// Default to first visible container
|
// Default to first visible container
|
||||||
$container = $containers[0];
|
$container = $containers[0];
|
||||||
|
|
||||||
// Click on newly expanded list element, indirectly setting the dropdown value
|
// Click on newly expanded list element, indirectly setting the dropdown value
|
||||||
$linkEl = $container->find('xpath', './/a[./@href]');
|
$linkEl = $container->find('xpath', './/a[./@href]');
|
||||||
assertNotNull($linkEl, 'Chosen.js link element not found');
|
assertNotNull($linkEl, 'Chosen.js link element not found');
|
||||||
$this->getSession()->wait(100); // wait for dropdown overlay to appear
|
$this->getSession()->wait(100); // wait for dropdown overlay to appear
|
||||||
$linkEl->click();
|
$linkEl->click();
|
||||||
|
|
||||||
$listEl = $container->find('xpath', sprintf('.//li[contains(normalize-space(string(.)), \'%s\')]', $value));
|
$listEl = $container->find('xpath', sprintf('.//li[contains(normalize-space(string(.)), \'%s\')]', $value));
|
||||||
assertNotNull($listEl, sprintf(
|
assertNotNull($listEl, sprintf(
|
||||||
'Chosen.js list element with title "%s" not found',
|
'Chosen.js list element with title "%s" not found',
|
||||||
$value
|
$value
|
||||||
));
|
));
|
||||||
|
|
||||||
// Dropdown flyout might be animated
|
// Dropdown flyout might be animated
|
||||||
// $this->getSession()->wait(1000, 'jQuery(":animated").length == 0');
|
// $this->getSession()->wait(1000, 'jQuery(":animated").length == 0');
|
||||||
$this->getSession()->wait(300);
|
$this->getSession()->wait(300);
|
||||||
|
|
||||||
$listEl->click();
|
$listEl->click();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns fixed step argument (with \\" replaced back to ").
|
* Returns fixed step argument (with \\" replaced back to ").
|
||||||
*
|
*
|
||||||
* @param string $argument
|
* @param string $argument
|
||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
protected function fixStepArgument($argument)
|
protected function fixStepArgument($argument)
|
||||||
{
|
{
|
||||||
return str_replace('\\"', '"', $argument);
|
return str_replace('\\"', '"', $argument);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,18 +27,18 @@ if(!defined('BASE_PATH')) define('BASE_PATH', dirname($frameworkPath));
|
|||||||
|
|
||||||
// Copied from cli-script.php, to enable same behaviour through phpunit runner.
|
// Copied from cli-script.php, to enable same behaviour through phpunit runner.
|
||||||
if(isset($_SERVER['argv'][2])) {
|
if(isset($_SERVER['argv'][2])) {
|
||||||
$args = array_slice($_SERVER['argv'],2);
|
$args = array_slice($_SERVER['argv'],2);
|
||||||
if(!isset($_GET)) $_GET = array();
|
if(!isset($_GET)) $_GET = array();
|
||||||
if(!isset($_REQUEST)) $_REQUEST = array();
|
if(!isset($_REQUEST)) $_REQUEST = array();
|
||||||
foreach($args as $arg) {
|
foreach($args as $arg) {
|
||||||
if(strpos($arg,'=') == false) {
|
if(strpos($arg,'=') == false) {
|
||||||
$_GET['args'][] = $arg;
|
$_GET['args'][] = $arg;
|
||||||
} else {
|
} else {
|
||||||
$newItems = array();
|
$newItems = array();
|
||||||
parse_str( (substr($arg,0,2) == '--') ? substr($arg,2) : $arg, $newItems );
|
parse_str( (substr($arg,0,2) == '--') ? substr($arg,2) : $arg, $newItems );
|
||||||
$_GET = array_merge($_GET, $newItems);
|
$_GET = array_merge($_GET, $newItems);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$_REQUEST = array_merge($_REQUEST, $_GET);
|
$_REQUEST = array_merge($_REQUEST, $_GET);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,4 +70,4 @@ if(!isset($_GET['flush']) || !$_GET['flush']) {
|
|||||||
"Add flush=1 as an argument to discover new classes or files.\n",
|
"Add flush=1 as an argument to discover new classes or files.\n",
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -10,8 +10,8 @@ class HTTPTest extends SapphireTest {
|
|||||||
/**
|
/**
|
||||||
* Tests {@link HTTP::getLinksIn()}
|
* Tests {@link HTTP::getLinksIn()}
|
||||||
*/
|
*/
|
||||||
public function testGetLinksIn() {
|
public function testGetLinksIn() {
|
||||||
$content = '
|
$content = '
|
||||||
<h2><a href="/">My Cool Site</a></h2>
|
<h2><a href="/">My Cool Site</a></h2>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
@ -26,13 +26,13 @@ class HTTPTest extends SapphireTest {
|
|||||||
played a part in his <a href=journey"extra id="JourneyLink">journey</a>. HE ALSO DISCOVERED THE
|
played a part in his <a href=journey"extra id="JourneyLink">journey</a>. HE ALSO DISCOVERED THE
|
||||||
<A HREF="CAPS LOCK">KEY</a>. Later he got his <a href="quotes \'mixed\' up">mixed up</a>.
|
<A HREF="CAPS LOCK">KEY</a>. Later he got his <a href="quotes \'mixed\' up">mixed up</a>.
|
||||||
</p>
|
</p>
|
||||||
';
|
';
|
||||||
|
|
||||||
$expected = array (
|
$expected = array (
|
||||||
'/', 'home/', 'mother/', '$Journey', 'space travel', 'unquoted', 'single quote', '/father', 'attributes',
|
'/', 'home/', 'mother/', '$Journey', 'space travel', 'unquoted', 'single quote', '/father', 'attributes',
|
||||||
'journey', 'CAPS LOCK', 'quotes \'mixed\' up'
|
'journey', 'CAPS LOCK', 'quotes \'mixed\' up'
|
||||||
);
|
);
|
||||||
|
|
||||||
$result = HTTP::getLinksIn($content);
|
$result = HTTP::getLinksIn($content);
|
||||||
|
|
||||||
// Results don't neccesarily come out in the order they are in the $content param.
|
// Results don't neccesarily come out in the order they are in the $content param.
|
||||||
@ -41,7 +41,7 @@ class HTTPTest extends SapphireTest {
|
|||||||
|
|
||||||
$this->assertTrue(is_array($result));
|
$this->assertTrue(is_array($result));
|
||||||
$this->assertEquals($expected, $result, 'Test that all links within the content are found.');
|
$this->assertEquals($expected, $result, 'Test that all links within the content are found.');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link HTTP::setGetVar()}
|
* Tests {@link HTTP::setGetVar()}
|
||||||
|
@ -11,7 +11,7 @@ class ArrayDataTest extends SapphireTest {
|
|||||||
$this->assertEquals("Varchar", get_class($arrayData->A));
|
$this->assertEquals("Varchar", get_class($arrayData->A));
|
||||||
$this->assertEquals("ArrayData", get_class($arrayData->B));
|
$this->assertEquals("ArrayData", get_class($arrayData->B));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testWrappingANonEmptyObjectWorks() {
|
public function testWrappingANonEmptyObjectWorks() {
|
||||||
$object = new ArrayDataTest_NonEmptyObject();
|
$object = new ArrayDataTest_NonEmptyObject();
|
||||||
$this->assertTrue(is_object($object));
|
$this->assertTrue(is_object($object));
|
||||||
|
@ -4,5 +4,5 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
namespace silverstripe\test;
|
namespace silverstripe\test;
|
||||||
|
|
||||||
class ClassB extends ClassA { }
|
class ClassB extends ClassA { }
|
||||||
|
@ -55,7 +55,7 @@ class CsvBulkLoaderTest extends SapphireTest {
|
|||||||
|
|
||||||
$this->assertEquals(4, $resultDataObject->Count(),
|
$this->assertEquals(4, $resultDataObject->Count(),
|
||||||
'Test if existing data is deleted before new data is added');
|
'Test if existing data is deleted before new data is added');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test import with manual column mapping
|
* Test import with manual column mapping
|
||||||
|
@ -18,11 +18,11 @@ class FileFieldTest extends FunctionalTest {
|
|||||||
new FieldList()
|
new FieldList()
|
||||||
);
|
);
|
||||||
$fileFieldValue = array(
|
$fileFieldValue = array(
|
||||||
'name' => 'aCV.txt',
|
'name' => 'aCV.txt',
|
||||||
'type' => 'application/octet-stream',
|
'type' => 'application/octet-stream',
|
||||||
'tmp_name' => '/private/var/tmp/phpzTQbqP',
|
'tmp_name' => '/private/var/tmp/phpzTQbqP',
|
||||||
'error' => 0,
|
'error' => 0,
|
||||||
'size' => 3471
|
'size' => 3471
|
||||||
);
|
);
|
||||||
$fileField->setValue($fileFieldValue);
|
$fileField->setValue($fileFieldValue);
|
||||||
|
|
||||||
@ -46,11 +46,11 @@ class FileFieldTest extends FunctionalTest {
|
|||||||
);
|
);
|
||||||
// All fields are filled but for some reason an error occured when uploading the file => fails
|
// All fields are filled but for some reason an error occured when uploading the file => fails
|
||||||
$fileFieldValue = array(
|
$fileFieldValue = array(
|
||||||
'name' => 'aCV.txt',
|
'name' => 'aCV.txt',
|
||||||
'type' => 'application/octet-stream',
|
'type' => 'application/octet-stream',
|
||||||
'tmp_name' => '/private/var/tmp/phpzTQbqP',
|
'tmp_name' => '/private/var/tmp/phpzTQbqP',
|
||||||
'error' => 1,
|
'error' => 1,
|
||||||
'size' => 3471
|
'size' => 3471
|
||||||
);
|
);
|
||||||
$fileField->setValue($fileFieldValue);
|
$fileField->setValue($fileFieldValue);
|
||||||
|
|
||||||
|
@ -368,7 +368,7 @@ class RequirementsTest extends SapphireTest {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function assertFileNotIncluded($backend, $type, $files) {
|
public function assertFileNotIncluded($backend, $type, $files) {
|
||||||
$type = strtolower($type);
|
$type = strtolower($type);
|
||||||
switch ($type) {
|
switch ($type) {
|
||||||
@ -412,4 +412,4 @@ class RequirementsTest extends SapphireTest {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
* @subpackage tests
|
* @subpackage tests
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class UploadFieldTest extends FunctionalTest {
|
class UploadFieldTest extends FunctionalTest {
|
||||||
|
|
||||||
static $fixture_file = 'UploadFieldTest.yml';
|
static $fixture_file = 'UploadFieldTest.yml';
|
||||||
|
|
||||||
|
@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
class SampleService
|
class SampleService
|
||||||
{
|
{
|
||||||
public $constructorVarOne;
|
public $constructorVarOne;
|
||||||
public $constructorVarTwo;
|
public $constructorVarTwo;
|
||||||
|
|
||||||
public function __construct($v1 = null, $v2 = null) {
|
public function __construct($v1 = null, $v2 = null) {
|
||||||
$this->constructorVarOne = $v1;
|
$this->constructorVarOne = $v1;
|
||||||
$this->constructorVarTwo = $v2;
|
$this->constructorVarTwo = $v2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@
|
|||||||
'<input type="hidden" name="testfield" value="1" />' +
|
'<input type="hidden" name="testfield" value="1" />' +
|
||||||
'</div>'
|
'</div>'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function() {
|
afterEach(function() {
|
||||||
$('#testfield').remove();
|
$('#testfield').remove();
|
||||||
@ -114,7 +114,7 @@
|
|||||||
'<input type="hidden" name="testfield" value="1" />' +
|
'<input type="hidden" name="testfield" value="1" />' +
|
||||||
'</div>'
|
'</div>'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function() {
|
afterEach(function() {
|
||||||
$('#testfield').remove();
|
$('#testfield').remove();
|
||||||
@ -146,7 +146,7 @@
|
|||||||
'<input type="hidden" name="testfield" value="4,5" />' +
|
'<input type="hidden" name="testfield" value="4,5" />' +
|
||||||
'</div>'
|
'</div>'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function() {
|
afterEach(function() {
|
||||||
$('#testfield').remove();
|
$('#testfield').remove();
|
||||||
@ -222,7 +222,7 @@
|
|||||||
'<input type="hidden" name="MyFormValue" value="foo" />' +
|
'<input type="hidden" name="MyFormValue" value="foo" />' +
|
||||||
'</form>'
|
'</form>'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function() {
|
afterEach(function() {
|
||||||
$('#testfield').remove();
|
$('#testfield').remove();
|
||||||
@ -236,4 +236,4 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}(jQuery));
|
}(jQuery));
|
||||||
|
@ -60,7 +60,6 @@ class ComponentSetTest_Player extends Member implements TestOnly {
|
|||||||
static $belongs_many_many = array(
|
static $belongs_many_many = array(
|
||||||
'Teams' => 'ComponentSetTest_Team'
|
'Teams' => 'ComponentSetTest_Team'
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class ComponentSetTest_Team extends DataObject implements TestOnly {
|
class ComponentSetTest_Team extends DataObject implements TestOnly {
|
||||||
|
@ -624,11 +624,11 @@ class DataListTest extends SapphireTest {
|
|||||||
*/
|
*/
|
||||||
public function testExcludeOnFilter() {
|
public function testExcludeOnFilter() {
|
||||||
$list = DataObjectTest_TeamComment::get();
|
$list = DataObjectTest_TeamComment::get();
|
||||||
$list = $list->filter('Comment', 'Phil is a unique guy, and comments on team2');
|
$list = $list->filter('Comment', 'Phil is a unique guy, and comments on team2');
|
||||||
$list = $list->exclude('Name', 'Bob');
|
$list = $list->exclude('Name', 'Bob');
|
||||||
|
|
||||||
$this->assertContains(
|
$this->assertContains(
|
||||||
'WHERE ("DataObjectTest_TeamComment"."Comment" = '
|
'WHERE ("DataObjectTest_TeamComment"."Comment" = '
|
||||||
. '\'Phil is a unique guy, and comments on team2\') '
|
. '\'Phil is a unique guy, and comments on team2\') '
|
||||||
. 'AND (("DataObjectTest_TeamComment"."Name" != \'Bob\'))',
|
. 'AND (("DataObjectTest_TeamComment"."Name" != \'Bob\'))',
|
||||||
$list->sql());
|
$list->sql());
|
||||||
|
@ -1125,7 +1125,6 @@ class DataObjectTest_Player extends Member implements TestOnly {
|
|||||||
static $belongs_many_many = array(
|
static $belongs_many_many = array(
|
||||||
'Teams' => 'DataObjectTest_Team'
|
'Teams' => 'DataObjectTest_Team'
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class DataObjectTest_Team extends DataObject implements TestOnly {
|
class DataObjectTest_Team extends DataObject implements TestOnly {
|
||||||
|
@ -71,12 +71,12 @@ class MoneyTest extends SapphireTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write a Money object to the database, then re-read it to ensure it
|
* Write a Money object to the database, then re-read it to ensure it
|
||||||
* is re-read properly.
|
* is re-read properly.
|
||||||
*/
|
*/
|
||||||
public function testGettingWrittenDataObject() {
|
public function testGettingWrittenDataObject() {
|
||||||
$local = i18n::get_locale();
|
$local = i18n::get_locale();
|
||||||
//make sure that the $ amount is not prefixed by US$, as it would be in non-US locale
|
//make sure that the $ amount is not prefixed by US$, as it would be in non-US locale
|
||||||
i18n::set_locale('en_US');
|
i18n::set_locale('en_US');
|
||||||
|
|
||||||
$obj = new MoneyTest_DataObject();
|
$obj = new MoneyTest_DataObject();
|
||||||
@ -99,8 +99,8 @@ class MoneyTest extends SapphireTest {
|
|||||||
"Money field not added to data object properly when read."
|
"Money field not added to data object properly when read."
|
||||||
);
|
);
|
||||||
|
|
||||||
i18n::set_locale($local);
|
i18n::set_locale($local);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testToCurrency() {
|
public function testToCurrency() {
|
||||||
$USD = new Money();
|
$USD = new Money();
|
||||||
|
@ -43,11 +43,11 @@ class PaginatedListTest extends SapphireTest {
|
|||||||
public function testSetPaginationFromQuery() {
|
public function testSetPaginationFromQuery() {
|
||||||
$query = $this->getMock('SQLQuery');
|
$query = $this->getMock('SQLQuery');
|
||||||
$query->expects($this->once())
|
$query->expects($this->once())
|
||||||
->method('getLimit')
|
->method('getLimit')
|
||||||
->will($this->returnValue(array('limit' => 15, 'start' => 30)));
|
->will($this->returnValue(array('limit' => 15, 'start' => 30)));
|
||||||
$query->expects($this->once())
|
$query->expects($this->once())
|
||||||
->method('unlimitedRowCount')
|
->method('unlimitedRowCount')
|
||||||
->will($this->returnValue(100));
|
->will($this->returnValue(100));
|
||||||
|
|
||||||
$list = new PaginatedList(new ArrayList());
|
$list = new PaginatedList(new ArrayList());
|
||||||
$list->setPaginationFromQuery($query);
|
$list->setPaginationFromQuery($query);
|
||||||
|
@ -254,12 +254,12 @@ class VersionedTest extends SapphireTest {
|
|||||||
* Test that SQLQuery::queriedTables() applies the version-suffixes properly.
|
* Test that SQLQuery::queriedTables() applies the version-suffixes properly.
|
||||||
*/
|
*/
|
||||||
public function testQueriedTables() {
|
public function testQueriedTables() {
|
||||||
Versioned::reading_stage('Live');
|
Versioned::reading_stage('Live');
|
||||||
|
|
||||||
$this->assertEquals(array(
|
$this->assertEquals(array(
|
||||||
'VersionedTest_DataObject_Live',
|
'VersionedTest_DataObject_Live',
|
||||||
'VersionedTest_Subclass_Live',
|
'VersionedTest_Subclass_Live',
|
||||||
), DataObject::get('VersionedTest_Subclass')->dataQuery()->query()->queriedTables());
|
), DataObject::get('VersionedTest_Subclass')->dataQuery()->query()->queriedTables());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetVersionWhenClassnameChanged() {
|
public function testGetVersionWhenClassnameChanged() {
|
||||||
|
18
tests/phpcs/tabs.xml
Normal file
18
tests/phpcs/tabs.xml
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ruleset name="SilverStripe.Tabs">
|
||||||
|
<description>CodeSniffer ruleset for SilverStripe indentation conventions.</description>
|
||||||
|
|
||||||
|
<!-- exclude SCSS-generated CSS files -->
|
||||||
|
<exclude-pattern>*/css/*</exclude-pattern>
|
||||||
|
<exclude-pattern>css/*</exclude-pattern>
|
||||||
|
|
||||||
|
<!-- exclude thirdparty content -->
|
||||||
|
<exclude-pattern>thirdparty/*</exclude-pattern>
|
||||||
|
<exclude-pattern>*/jquery-changetracker/*</exclude-pattern>
|
||||||
|
<exclude-pattern>parsers/HTML/BBCodeParser/*</exclude-pattern>
|
||||||
|
|
||||||
|
<!-- PHP-PEG generated file not intended for human consumption -->
|
||||||
|
<exclude-pattern>*/SSTemplateParser.php$</exclude-pattern>
|
||||||
|
<rule ref="Generic.WhiteSpace.DisallowSpaceIndent"/>
|
||||||
|
</ruleset>
|
||||||
|
|
@ -41,40 +41,40 @@ class SearchContextTest extends SapphireTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function testPartialMatchUsedByDefaultWhenNotExplicitlySet() {
|
public function testPartialMatchUsedByDefaultWhenNotExplicitlySet() {
|
||||||
$person = singleton('SearchContextTest_Person');
|
$person = singleton('SearchContextTest_Person');
|
||||||
$context = $person->getDefaultSearchContext();
|
$context = $person->getDefaultSearchContext();
|
||||||
|
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
array(
|
array(
|
||||||
"Name" => new PartialMatchFilter("Name"),
|
"Name" => new PartialMatchFilter("Name"),
|
||||||
"HairColor" => new PartialMatchFilter("HairColor"),
|
"HairColor" => new PartialMatchFilter("HairColor"),
|
||||||
"EyeColor" => new PartialMatchFilter("EyeColor")
|
"EyeColor" => new PartialMatchFilter("EyeColor")
|
||||||
),
|
),
|
||||||
$context->getFilters()
|
$context->getFilters()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testDefaultFiltersDefinedWhenNotSetInDataObject() {
|
public function testDefaultFiltersDefinedWhenNotSetInDataObject() {
|
||||||
$book = singleton('SearchContextTest_Book');
|
$book = singleton('SearchContextTest_Book');
|
||||||
$context = $book->getDefaultSearchContext();
|
$context = $book->getDefaultSearchContext();
|
||||||
|
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
array(
|
array(
|
||||||
"Title" => new PartialMatchFilter("Title")
|
"Title" => new PartialMatchFilter("Title")
|
||||||
),
|
),
|
||||||
$context->getFilters()
|
$context->getFilters()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testUserDefinedFiltersAppearInSearchContext() {
|
public function testUserDefinedFiltersAppearInSearchContext() {
|
||||||
$company = singleton('SearchContextTest_Company');
|
$company = singleton('SearchContextTest_Company');
|
||||||
$context = $company->getDefaultSearchContext();
|
$context = $company->getDefaultSearchContext();
|
||||||
|
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
array(
|
array(
|
||||||
"Name" => new PartialMatchFilter("Name"),
|
"Name" => new PartialMatchFilter("Name"),
|
||||||
"Industry" => new PartialMatchFilter("Industry"),
|
"Industry" => new PartialMatchFilter("Industry"),
|
||||||
"AnnualProfit" => new PartialMatchFilter("AnnualProfit")
|
"AnnualProfit" => new PartialMatchFilter("AnnualProfit")
|
||||||
),
|
),
|
||||||
$context->getFilters()
|
$context->getFilters()
|
||||||
);
|
);
|
||||||
@ -87,8 +87,8 @@ class SearchContextTest extends SapphireTest {
|
|||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
new FieldList(
|
new FieldList(
|
||||||
new TextField("Name", 'Name'),
|
new TextField("Name", 'Name'),
|
||||||
new TextareaField("Industry", 'Industry'),
|
new TextareaField("Industry", 'Industry'),
|
||||||
new NumericField("AnnualProfit", 'The Almighty Annual Profit')
|
new NumericField("AnnualProfit", 'The Almighty Annual Profit')
|
||||||
),
|
),
|
||||||
$context->getFields()
|
$context->getFields()
|
||||||
);
|
);
|
||||||
|
@ -52,47 +52,47 @@ class GroupTest extends FunctionalTest {
|
|||||||
public function testMemberGroupRelationForm() {
|
public function testMemberGroupRelationForm() {
|
||||||
Session::set('loggedInAs', $this->idFromFixture('GroupTest_Member', 'admin'));
|
Session::set('loggedInAs', $this->idFromFixture('GroupTest_Member', 'admin'));
|
||||||
|
|
||||||
$adminGroup = $this->objFromFixture('Group', 'admingroup');
|
$adminGroup = $this->objFromFixture('Group', 'admingroup');
|
||||||
$parentGroup = $this->objFromFixture('Group', 'parentgroup');
|
$parentGroup = $this->objFromFixture('Group', 'parentgroup');
|
||||||
$childGroup = $this->objFromFixture('Group', 'childgroup');
|
$childGroup = $this->objFromFixture('Group', 'childgroup');
|
||||||
|
|
||||||
// Test single group relation through checkboxsetfield
|
// Test single group relation through checkboxsetfield
|
||||||
$form = new GroupTest_MemberForm($this, 'Form');
|
$form = new GroupTest_MemberForm($this, 'Form');
|
||||||
$member = $this->objFromFixture('GroupTest_Member', 'admin');
|
$member = $this->objFromFixture('GroupTest_Member', 'admin');
|
||||||
$form->loadDataFrom($member);
|
$form->loadDataFrom($member);
|
||||||
$checkboxSetField = $form->Fields()->fieldByName('Groups');
|
$checkboxSetField = $form->Fields()->fieldByName('Groups');
|
||||||
$checkboxSetField->setValue(array(
|
$checkboxSetField->setValue(array(
|
||||||
$adminGroup->ID => $adminGroup->ID, // keep existing relation
|
$adminGroup->ID => $adminGroup->ID, // keep existing relation
|
||||||
$parentGroup->ID => $parentGroup->ID, // add new relation
|
$parentGroup->ID => $parentGroup->ID, // add new relation
|
||||||
));
|
));
|
||||||
$form->saveInto($member);
|
$form->saveInto($member);
|
||||||
$updatedGroups = $member->Groups();
|
$updatedGroups = $member->Groups();
|
||||||
|
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
array($adminGroup->ID, $parentGroup->ID),
|
array($adminGroup->ID, $parentGroup->ID),
|
||||||
$updatedGroups->column(),
|
$updatedGroups->column(),
|
||||||
"Adding a toplevel group works"
|
"Adding a toplevel group works"
|
||||||
);
|
);
|
||||||
|
|
||||||
// Test unsetting relationship
|
// Test unsetting relationship
|
||||||
$form->loadDataFrom($member);
|
$form->loadDataFrom($member);
|
||||||
$checkboxSetField = $form->Fields()->fieldByName('Groups');
|
$checkboxSetField = $form->Fields()->fieldByName('Groups');
|
||||||
$checkboxSetField->setValue(array(
|
$checkboxSetField->setValue(array(
|
||||||
$adminGroup->ID => $adminGroup->ID, // keep existing relation
|
$adminGroup->ID => $adminGroup->ID, // keep existing relation
|
||||||
//$parentGroup->ID => $parentGroup->ID, // remove previously set relation
|
//$parentGroup->ID => $parentGroup->ID, // remove previously set relation
|
||||||
));
|
));
|
||||||
$form->saveInto($member);
|
$form->saveInto($member);
|
||||||
$member->flushCache();
|
$member->flushCache();
|
||||||
$updatedGroups = $member->Groups();
|
$updatedGroups = $member->Groups();
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
array($adminGroup->ID),
|
array($adminGroup->ID),
|
||||||
$updatedGroups->column(),
|
$updatedGroups->column(),
|
||||||
"Removing a previously added toplevel group works"
|
"Removing a previously added toplevel group works"
|
||||||
);
|
);
|
||||||
|
|
||||||
// Test adding child group
|
// Test adding child group
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testCollateAncestorIDs() {
|
public function testCollateAncestorIDs() {
|
||||||
$parentGroup = $this->objFromFixture('Group', 'parentgroup');
|
$parentGroup = $this->objFromFixture('Group', 'parentgroup');
|
||||||
@ -139,37 +139,37 @@ class GroupTest extends FunctionalTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class GroupTest_Member extends Member implements TestOnly {
|
class GroupTest_Member extends Member implements TestOnly {
|
||||||
|
|
||||||
public function getCMSFields() {
|
public function getCMSFields() {
|
||||||
$groups = DataObject::get('Group');
|
$groups = DataObject::get('Group');
|
||||||
$groupsMap = ($groups) ? $groups->map() : false;
|
$groupsMap = ($groups) ? $groups->map() : false;
|
||||||
$fields = new FieldList(
|
$fields = new FieldList(
|
||||||
new HiddenField('ID', 'ID'),
|
new HiddenField('ID', 'ID'),
|
||||||
new CheckboxSetField(
|
new CheckboxSetField(
|
||||||
'Groups',
|
'Groups',
|
||||||
'Groups',
|
'Groups',
|
||||||
$groupsMap
|
$groupsMap
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
return $fields;
|
return $fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class GroupTest_MemberForm extends Form {
|
class GroupTest_MemberForm extends Form {
|
||||||
|
|
||||||
public function __construct($controller, $name) {
|
public function __construct($controller, $name) {
|
||||||
$fields = singleton('GroupTest_Member')->getCMSFields();
|
$fields = singleton('GroupTest_Member')->getCMSFields();
|
||||||
$actions = new FieldList(
|
$actions = new FieldList(
|
||||||
new FormAction('doSave','save')
|
new FormAction('doSave','save')
|
||||||
);
|
);
|
||||||
|
|
||||||
parent::__construct($controller, $name, $fields, $actions);
|
parent::__construct($controller, $name, $fields, $actions);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function doSave($data, $form) {
|
public function doSave($data, $form) {
|
||||||
// done in testing methods
|
// done in testing methods
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -29,8 +29,8 @@ class MemberTest extends FunctionalTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function __destruct() {
|
public function __destruct() {
|
||||||
i18n::set_default_locale($this->local);
|
i18n::set_default_locale($this->local);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setUp() {
|
public function setUp() {
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
|
@ -155,9 +155,9 @@ class ViewableDataTest_Castable extends ViewableData {
|
|||||||
return $this->unsafeXML();
|
return $this->unsafeXML();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function forTemplate() {
|
public function forTemplate() {
|
||||||
return 'castable';
|
return 'castable';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ViewableDataTest_RequiresCasting extends ViewableData {
|
class ViewableDataTest_RequiresCasting extends ViewableData {
|
||||||
|
@ -65,7 +65,7 @@ class ArrayData extends ViewableData {
|
|||||||
return new ArrayData($value);
|
return new ArrayData($value);
|
||||||
} elseif (ArrayLib::is_associative($value)) {
|
} elseif (ArrayLib::is_associative($value)) {
|
||||||
return new ArrayData($value);
|
return new ArrayData($value);
|
||||||
} else {
|
} else {
|
||||||
return $value;
|
return $value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ class Requirements {
|
|||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
public static function get_combined_files_enabled() {
|
public static function get_combined_files_enabled() {
|
||||||
return self::backend()->get_combined_files_enabled();
|
return self::backend()->get_combined_files_enabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -656,7 +656,7 @@ class Requirements_Backend {
|
|||||||
$jsRequirements = '';
|
$jsRequirements = '';
|
||||||
|
|
||||||
// Combine files - updates $this->javascript and $this->css
|
// Combine files - updates $this->javascript and $this->css
|
||||||
$this->process_combined_files();
|
$this->process_combined_files();
|
||||||
|
|
||||||
foreach(array_diff_key($this->javascript,$this->blocked) as $file => $dummy) {
|
foreach(array_diff_key($this->javascript,$this->blocked) as $file => $dummy) {
|
||||||
$path = $this->path_for_file($file);
|
$path = $this->path_for_file($file);
|
||||||
@ -1022,9 +1022,9 @@ class Requirements_Backend {
|
|||||||
// file exists, check modification date of every contained file
|
// file exists, check modification date of every contained file
|
||||||
$srcLastMod = 0;
|
$srcLastMod = 0;
|
||||||
foreach($fileList as $file) {
|
foreach($fileList as $file) {
|
||||||
if(file_exists($base . $file)) {
|
if(file_exists($base . $file)) {
|
||||||
$srcLastMod = max(filemtime($base . $file), $srcLastMod);
|
$srcLastMod = max(filemtime($base . $file), $srcLastMod);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$refresh = $srcLastMod > filemtime($combinedFilePath);
|
$refresh = $srcLastMod > filemtime($combinedFilePath);
|
||||||
} else {
|
} else {
|
||||||
@ -1070,9 +1070,9 @@ class Requirements_Backend {
|
|||||||
// method repeatedly - it will behave different on the second call!
|
// method repeatedly - it will behave different on the second call!
|
||||||
$this->javascript = $newJSRequirements;
|
$this->javascript = $newJSRequirements;
|
||||||
$this->css = $newCSSRequirements;
|
$this->css = $newCSSRequirements;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function get_custom_scripts() {
|
public function get_custom_scripts() {
|
||||||
$requirements = "";
|
$requirements = "";
|
||||||
|
|
||||||
if($this->customScript) {
|
if($this->customScript) {
|
||||||
|
@ -453,7 +453,7 @@ class SSTemplateParser extends Parser {
|
|||||||
function Require_Call(&$res, $sub) {
|
function Require_Call(&$res, $sub) {
|
||||||
$res['php'] = "Requirements::".$sub['Method']['text'].'('.$sub['CallArguments']['php'].');';
|
$res['php'] = "Requirements::".$sub['Method']['text'].'('.$sub['CallArguments']['php'].');';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*!*
|
/*!*
|
||||||
|
|
||||||
@ -617,7 +617,7 @@ class SSTemplateParser extends Parser {
|
|||||||
function OldTTag_OldTPart(&$res, $sub) {
|
function OldTTag_OldTPart(&$res, $sub) {
|
||||||
$res['php'] = $sub['php'];
|
$res['php'] = $sub['php'];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!*
|
/*!*
|
||||||
|
|
||||||
# This is the old <% sprintf(_t()) %> tag
|
# This is the old <% sprintf(_t()) %> tag
|
||||||
|
@ -664,10 +664,10 @@ class SSViewer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(!$this->chosenTemplates) {
|
if(!$this->chosenTemplates) {
|
||||||
$templateList = (is_array($templateList)) ? $templateList : array($templateList);
|
$templateList = (is_array($templateList)) ? $templateList : array($templateList);
|
||||||
|
|
||||||
user_error("None of these templates can be found in theme '"
|
user_error("None of these templates can be found in theme '"
|
||||||
. self::current_theme() . "': ". implode(".ss, ", $templateList) . ".ss", E_USER_WARNING);
|
. self::current_theme() . "': ". implode(".ss, ", $templateList) . ".ss", E_USER_WARNING);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -717,7 +717,7 @@ class SSViewer {
|
|||||||
protected static $options = array(
|
protected static $options = array(
|
||||||
'rewriteHashlinks' => true,
|
'rewriteHashlinks' => true,
|
||||||
);
|
);
|
||||||
|
|
||||||
protected static $topLevel = array();
|
protected static $topLevel = array();
|
||||||
|
|
||||||
public static function topLevel() {
|
public static function topLevel() {
|
||||||
|
Loading…
Reference in New Issue
Block a user