From cd4ebb25ca59b15e216ce2e546b7c5cf2e6b5e51 Mon Sep 17 00:00:00 2001 From: James Goodman Date: Thu, 10 Oct 2013 08:04:03 +0000 Subject: [PATCH 001/119] Fix HTMLText AbsoluteLinks not parsing placeholder Add an AbsoluteLinks method to HTMLText that parses placeholders instead of returning the raw value. --- model/fieldtypes/HTMLText.php | 12 ++++++++-- tests/api/RSSFeedTest.php | 45 ++++++++++++++++++++++++++++++++--- 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/model/fieldtypes/HTMLText.php b/model/fieldtypes/HTMLText.php index 46d462aa8..b2c8b1885 100644 --- a/model/fieldtypes/HTMLText.php +++ b/model/fieldtypes/HTMLText.php @@ -172,8 +172,16 @@ class HTMLText extends Text { /* If we didn't find a sentence ending, use the summary. We re-call rather than using paragraph so that * Summary will limit the result this time */ return $this->Summary(); - } - + } + + /** + * Return the value of the field with relative links converted to absolute urls (with placeholders parsed). + * @return string + */ + public function AbsoluteLinks() { + return HTTP::absoluteURLs($this->forTemplate()); + } + public function forTemplate() { if ($this->processShortcodes) { return ShortcodeParser::get_active()->parse($this->value); diff --git a/tests/api/RSSFeedTest.php b/tests/api/RSSFeedTest.php index 57032965a..0402bd30a 100644 --- a/tests/api/RSSFeedTest.php +++ b/tests/api/RSSFeedTest.php @@ -16,7 +16,6 @@ class RSSFeedTest extends SapphireTest { $rssFeed = new RSSFeed($list, "http://www.example.com", "Test RSS Feed", "Test RSS Feed Description"); $content = $rssFeed->outputToBrowser(); - //Debug::message($content); $this->assertContains('http://www.example.org/item-a/', $content); $this->assertContains('http://www.example.com/item-b.html', $content); $this->assertContains('http://www.example.com/item-c.html', $content); @@ -44,6 +43,23 @@ class RSSFeedTest extends SapphireTest { $this->assertContains('ItemC AltContent', $content); } + public function testRSSFeedWithShortcode() { + $list = new ArrayList(); + $list->push(new RSSFeedTest_ItemD()); + + $rssFeed = new RSSFeed($list, "http://www.example.com", "Test RSS Feed", "Test RSS Feed Description"); + $content = $rssFeed->outputToBrowser(); + + $this->assertContains('http://www.example.org/item-d.html', $content); + + $this->assertContains('ItemD', $content); + + $this->assertContains( + '<p>ItemD Content test shortcode output</p>', + $content + ); + } + public function testRenderWithTemplate() { $rssFeed = new RSSFeed(new ArrayList(), "", "", ""); $rssFeed->setTemplate('RSSFeedTest'); @@ -61,6 +77,10 @@ class RSSFeedTest extends SapphireTest { Config::inst()->update('Director', 'alternate_base_url', '/'); if(!self::$original_host) self::$original_host = $_SERVER['HTTP_HOST']; $_SERVER['HTTP_HOST'] = 'www.example.org'; + + ShortcodeParser::get('default')->register('test_shortcode', function() { + return 'test shortcode output'; + }); } public function tearDown() { @@ -77,7 +97,7 @@ class RSSFeedTest_ItemA extends ViewableData { 'Content' => 'Text', 'AltContent' => 'Text', ); - + public function getTitle() { return "ItemA"; } @@ -89,7 +109,7 @@ class RSSFeedTest_ItemA extends ViewableData { public function getAltContent() { return "ItemA AltContent"; } - + public function Link($action = null) { return Controller::join_links("item-a/", $action); } @@ -134,3 +154,22 @@ class RSSFeedTest_ItemC extends ViewableData { return "http://www.example.com/item-c.html"; } } + +class RSSFeedTest_ItemD extends ViewableData { + // ItemD test fields - all fields use casting but Content & AltContent cast as HTMLText + private static $casting = array( + 'Title' => 'Varchar', + 'Content' => 'HTMLText' + ); + + public $Title = 'ItemD'; + public $Content = '

ItemD Content [test_shortcode]

'; + + public function Link() { + return 'item-d.html'; + } + + public function AbsoluteLink() { + return 'http://www.example.org/item-d.html'; + } +} From 0096ab0d3d1ba07dffbc025eb1677b299180bad7 Mon Sep 17 00:00:00 2001 From: Devlin Date: Tue, 4 Mar 2014 13:43:08 +0100 Subject: [PATCH 002/119] Upload: refactor file versioning --- filesystem/Upload.php | 38 ++++++++++++++--------- tests/filesystem/UploadTest.php | 55 ++++++++++++++++++++++++++++++++- 2 files changed, 77 insertions(+), 16 deletions(-) diff --git a/filesystem/Upload.php b/filesystem/Upload.php index ec8b7ca12..6b0003b9c 100644 --- a/filesystem/Upload.php +++ b/filesystem/Upload.php @@ -134,9 +134,10 @@ class Upload extends Controller { $file = $nameFilter->filter($tmpFile['name']); $fileName = basename($file); - $relativeFilePath = $parentFolder - ? $parentFolder->getRelativePath() . "$fileName" - : ASSETS_DIR . "/" . $fileName; + $relativeFolderPath = $parentFolder + ? $parentFolder->getRelativePath() + : ASSETS_DIR . '/'; + $relativeFilePath = $relativeFolderPath . $fileName; // Create a new file record (or try to retrieve an existing one) if(!$this->file) { @@ -156,26 +157,33 @@ class Upload extends Controller { } } - // if filename already exists, version the filename (e.g. test.gif to test1.gif) + // if filename already exists, version the filename (e.g. test.gif to test2.gif, test2.gif to test3.gif) if(!$this->replaceFile) { + $fileSuffixArray = explode('.', $fileName); + $fileTitle = array_shift($fileSuffixArray); + $fileSuffix = !empty($fileSuffixArray) + ? '.' . implode('.', $fileSuffixArray) + : null; + + // make sure files retain valid extensions + $oldFilePath = $relativeFilePath; + $relativeFilePath = $relativeFolderPath . $fileTitle . $fileSuffix; + if($oldFilePath !== $relativeFilePath) { + user_error("Couldn't fix $relativeFilePath", E_USER_ERROR); + } while(file_exists("$base/$relativeFilePath")) { $i = isset($i) ? ($i+1) : 2; - $oldFilePath = $relativeFilePath; - // make sure archives retain valid extensions - if(substr($relativeFilePath, strlen($relativeFilePath) - strlen('.tar.gz')) == '.tar.gz' || - substr($relativeFilePath, strlen($relativeFilePath) - strlen('.tar.bz2')) == '.tar.bz2') { - $relativeFilePath = preg_replace('/[0-9]*(\.tar\.[^.]+$)/', $i . '\\1', $relativeFilePath); - } else if (strpos($relativeFilePath, '.') !== false) { - $relativeFilePath = preg_replace('/[0-9]*(\.[^.]+$)/', $i . '\\1', $relativeFilePath); - } else if (strpos($relativeFilePath, '_') !== false) { - $relativeFilePath = preg_replace('/_([^_]+$)/', '_'.$i, $relativeFilePath); + $pattern = '/([0-9]+$)/'; + if(preg_match($pattern, $fileTitle)) { + $fileTitle = preg_replace($pattern, $i, $fileTitle); } else { - $relativeFilePath .= '_'.$i; + $fileTitle .= $i; } + $relativeFilePath = $relativeFolderPath . $fileTitle . $fileSuffix; if($oldFilePath == $relativeFilePath && $i > 2) { user_error("Couldn't fix $relativeFilePath with $i tries", E_USER_ERROR); } - } + } } else { //reset the ownerID to the current member when replacing files $this->file->OwnerID = (Member::currentUser() ? Member::currentUser()->ID : 0); diff --git a/tests/filesystem/UploadTest.php b/tests/filesystem/UploadTest.php index 48a833027..aa825948c 100644 --- a/tests/filesystem/UploadTest.php +++ b/tests/filesystem/UploadTest.php @@ -258,6 +258,10 @@ class UploadTest extends SapphireTest { $file->Name, 'File has a name without a number because it\'s not a duplicate' ); + $this->assertFileExists( + BASE_PATH . '/' . $file->getRelativePath(), + 'File exists' + ); $u = new Upload(); $u->load($tmpFile); @@ -267,9 +271,37 @@ class UploadTest extends SapphireTest { $file2->Name, 'File receives a number attached to the end before the extension' ); + $this->assertFileExists( + BASE_PATH . '/' . $file2->getRelativePath(), + 'File exists' + ); + $this->assertGreaterThan( + $file->ID, + $file2->ID, + 'File database record is not the same' + ); + + $u = new Upload(); + $u->load($tmpFile); + $file3 = $u->getFile(); + $this->assertEquals( + 'UploadTest-testUpload3.tar.gz', + $file3->Name, + 'File receives a number attached to the end before the extension' + ); + $this->assertFileExists( + BASE_PATH . '/' . $file3->getRelativePath(), + 'File exists' + ); + $this->assertGreaterThan( + $file2->ID, + $file3->ID, + 'File database record is not the same' + ); $file->delete(); $file2->delete(); + $file3->delete(); } public function testUploadFileWithNoExtensionTwiceAppendsNumber() { @@ -307,16 +339,29 @@ class UploadTest extends SapphireTest { $file->Name, 'File is uploaded without extension' ); + $this->assertFileExists( + BASE_PATH . '/' . $file->getRelativePath(), + 'File exists' + ); $u = new Upload(); $u->setValidator($v); $u->load($tmpFile); $file2 = $u->getFile(); $this->assertEquals( - 'UploadTest-testUpload-2', + 'UploadTest-testUpload2', $file2->Name, 'File receives a number attached to the end' ); + $this->assertFileExists( + BASE_PATH . '/' . $file2->getRelativePath(), + 'File exists' + ); + $this->assertGreaterThan( + $file->ID, + $file2->ID, + 'File database record is not the same' + ); $file->delete(); $file2->delete(); @@ -357,6 +402,10 @@ class UploadTest extends SapphireTest { $file->Name, 'File is uploaded without extension' ); + $this->assertFileExists( + BASE_PATH . '/' . $file->getRelativePath(), + 'File exists' + ); $u = new Upload(); $u->setValidator($v); @@ -368,6 +417,10 @@ class UploadTest extends SapphireTest { $file2->Name, 'File does not receive new name' ); + $this->assertFileExists( + BASE_PATH . '/' . $file2->getRelativePath(), + 'File exists' + ); $this->assertEquals( $file->ID, $file2->ID, From 58f8c2944a19bb88ed139d7f78f2c63ab1145d02 Mon Sep 17 00:00:00 2001 From: Loz Calver Date: Tue, 18 Feb 2014 22:20:18 +0000 Subject: [PATCH 003/119] Fix: Incorrect calculation of UploadField height (fixes #2862) --- css/AssetUploadField.css | 6 ++++-- javascript/UploadField.js | 28 ++++++++++------------------ scss/AssetUploadField.scss | 10 ++++++++-- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/css/AssetUploadField.css b/css/AssetUploadField.css index 7fb70211d..991d55f28 100644 --- a/css/AssetUploadField.css +++ b/css/AssetUploadField.css @@ -19,10 +19,12 @@ Used in side panels and action tabs #Form_EditorToolbarMediaForm .ui-tabs-panel { padding-left: 0px; } -body.cms.ss-uploadfield-edit-iframe, .composite.ss-assetuploadfield .details fieldset { padding: 16px; overflow: auto; background: #E2E2E2; } +body.cms.ss-uploadfield-edit-iframe, .composite.ss-assetuploadfield .details fieldset { overflow: auto; background: #E2E2E2; } body.cms.ss-uploadfield-edit-iframe span.readonly, .composite.ss-assetuploadfield .details fieldset span.readonly { font-style: italic; color: #777777; text-shadow: 0px 1px 0px #fff; } body.cms.ss-uploadfield-edit-iframe .fieldholder-small label, .composite.ss-assetuploadfield .details fieldset .fieldholder-small label { margin-left: 0; } +.composite.ss-assetuploadfield .details fieldset { padding: 16px; } + .ss-assetuploadfield h3 { border-bottom: 1px solid rgba(201, 205, 206, 0.8); -webkit-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.8); -moz-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.8); box-shadow: 0 1px 0 rgba(255, 255, 255, 0.8); margin: 0 0 8px; padding: 0 0 7px; clear: both; position: relative; } .ss-assetuploadfield .field { border-bottom: 0; box-shadow: none; } .ss-assetuploadfield .fileOverview { clear: both; margin-top: 10px; position: relative; } @@ -69,7 +71,7 @@ body.cms.ss-uploadfield-edit-iframe .fieldholder-small label, .composite.ss-asse .ss-assetuploadfield .ss-uploadfield-files .ss-uploadfield-item-editform { /* don't use display none, for it will break jQuery('iframe').contents().height() */ height: 0; overflow: hidden; clear: both; } .ss-assetuploadfield .ss-uploadfield-files .ss-uploadfield-item-editform.loading { width: 100%; height: 22px; padding: 15px 0; background: url(../admin/images/spinner.gif) no-repeat 50% 50%; } .ss-assetuploadfield .ss-uploadfield-files .ss-uploadfield-item-editform.loading iframe { /* Old IE needs this or it'll give the iframe a white background, covering the spinner */ padding-top: 0; margin-top: 37px; border: none; } -.ss-assetuploadfield .ss-uploadfield-files .ss-uploadfield-item-editform iframe { width: 100%; } +.ss-assetuploadfield .ss-uploadfield-files .ss-uploadfield-item-editform iframe { width: 100%; padding: 16px; background: #E2E2E2; } .ss-assetuploadfield .ss-uploadfield-addfile .ss-uploadfield-item-info { float: left; margin: 25px 0 0; } .ss-insert-media .ss-assetuploadfield .ss-uploadfield-addfile .ss-uploadfield-item-info { margin: 10px 0px 0 20px; } .ss-assetuploadfield .ss-uploadfield-addfile .ss-uploadfield-item-info label { font-size: 18px; line-height: 30px; padding: 0px 16px; margin-right: 0px; } diff --git a/javascript/UploadField.js b/javascript/UploadField.js index 5f791306c..33e70de6c 100644 --- a/javascript/UploadField.js +++ b/javascript/UploadField.js @@ -489,27 +489,19 @@ $('div.ss-upload .ss-uploadfield-item-editform').entwine({ fitHeight: function() { - var iframe = this.find('iframe'), padding = 32, parentPadding = 2; - var h = iframe.contents().find('form').height() + padding; + var iframe = this.find('iframe'), + contents = iframe.contents().find('body'), + bodyH = contents.find('form').outerHeight(true), // We set the height to match the form's outer height + iframeH = bodyH + (iframe.outerHeight(true) - iframe.height()), // content's height + padding on iframe elem + containerH = iframeH + (this.outerHeight(true) - this.height()); // iframe height + padding on container elem - if(this.hasClass('includeParent')){ - padding=0; - parentPadding=12; - } - - /* Set height of body except in IE8. Setting this in IE8 breaks the - dropdown */ - if(!$.browser.msie && $.browser.version.slice(0,3) != "8.0"){ - iframe.contents().find('body').css({'height':(h-padding)}); + /* Set height of body except in IE8. Setting this in IE8 breaks the dropdown */ + if( ! $.browser.msie && $.browser.version.slice(0,3) != "8.0"){ + contents.find('body').css({'height': bodyH}); } - // Set iframe to match its contents height - iframe.height(h); - - // set container to match the same height - iframe.parent().animate({height: h+parentPadding}, 500); - iframe.contents().find('body form').css({'width':'98%'}); - + iframe.height(iframeH); + this.animate({height: containerH}, 500); }, toggleEditForm: function() { var itemInfo = this.prev('.ss-uploadfield-item-info'), status = itemInfo.find('.ss-uploadfield-item-status'); diff --git a/scss/AssetUploadField.scss b/scss/AssetUploadField.scss index fcc64af6d..0e8f78ddb 100644 --- a/scss/AssetUploadField.scss +++ b/scss/AssetUploadField.scss @@ -33,8 +33,7 @@ } } -body.cms.ss-uploadfield-edit-iframe, .composite.ss-assetuploadfield .details fieldset { - padding: $grid-x*2; +body.cms.ss-uploadfield-edit-iframe { overflow: auto; background: #E2E2E2; @@ -51,6 +50,11 @@ body.cms.ss-uploadfield-edit-iframe, .composite.ss-assetuploadfield .details fie } } +.composite.ss-assetuploadfield .details fieldset { + @extend body.cms.ss-uploadfield-edit-iframe; + padding: $grid-x*2; +} + .ss-assetuploadfield { h3 { border-bottom: 1px solid $color-shadow-light; @@ -278,6 +282,8 @@ body.cms.ss-uploadfield-edit-iframe, .composite.ss-assetuploadfield .details fie iframe { width: 100%; + padding: $grid-x*2; + background: #E2E2E2; } } } From a3fb47a929a6f15b5fe80a70bf9db2cd796642f8 Mon Sep 17 00:00:00 2001 From: Loz Calver Date: Tue, 18 Feb 2014 22:24:35 +0000 Subject: [PATCH 004/119] Fix: Remove superfluous iframe resize for UploadField (fixes #2863) --- javascript/UploadField.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/javascript/UploadField.js b/javascript/UploadField.js index 33e70de6c..04095c4fc 100644 --- a/javascript/UploadField.js +++ b/javascript/UploadField.js @@ -439,8 +439,6 @@ self._prepareIframe(iframe, editform, itemInfo); iframe.data('src', ''); } - - if (editform.hasClass('opened')) editform.fitHeight(); }); } else { self._prepareIframe(iframe, editform, itemInfo); From b3d52de53111bc302d12f78f5cc75d04065f5d99 Mon Sep 17 00:00:00 2001 From: Igor Nadj Date: Wed, 6 Aug 2014 16:29:52 +1200 Subject: [PATCH 005/119] ENH making /dev extendable --- _config/dev.yml | 23 +++++ dev/DevBuildController.php | 32 +++++++ dev/DevelopmentAdmin.php | 126 ++++++++++++++++----------- tests/dev/DevAdminControllerTest.php | 116 ++++++++++++++++++++++++ 4 files changed, 244 insertions(+), 53 deletions(-) create mode 100644 _config/dev.yml create mode 100644 dev/DevBuildController.php create mode 100644 tests/dev/DevAdminControllerTest.php diff --git a/_config/dev.yml b/_config/dev.yml new file mode 100644 index 000000000..a3aa0b604 --- /dev/null +++ b/_config/dev.yml @@ -0,0 +1,23 @@ +--- +Name: DevelopmentAdmin +--- +DevelopmentAdmin: + registered_controllers: + build: + controller: 'DevBuildController' + links: + build: 'Build/rebuild this environment. Call this whenever you have updated your project sources' + tests: + controller: 'TestRunner' + links: + tests: 'See a list of unit tests to run' + 'tests/all': 'Run all tests' + jstests: + controller: 'JSTestRunner' + links: + jstests: 'See a list of JavaScript tests to run' + 'jstests/all': 'Run all JavaScript tests' + tasks: + controller: 'TaskRunner' + links: + tasks: 'See a list of build tasks to run' \ No newline at end of file diff --git a/dev/DevBuildController.php b/dev/DevBuildController.php new file mode 100644 index 000000000..02ffbaa52 --- /dev/null +++ b/dev/DevBuildController.php @@ -0,0 +1,32 @@ + 'build' + ); + + private static $allowed_actions = array( + 'build' + ); + + + public function build($request) { + if(Director::is_cli()) { + $da = DatabaseAdmin::create(); + return $da->handleRequest($request, $this->model); + } else { + $renderer = DebugView::create(); + $renderer->writeHeader(); + $renderer->writeInfo("Environment Builder", Director::absoluteBaseURL()); + echo "
"; + + $da = DatabaseAdmin::create(); + return $da->handleRequest($request, $this->model); + + echo "
"; + $renderer->writeFooter(); + } + } + +} diff --git a/dev/DevelopmentAdmin.php b/dev/DevelopmentAdmin.php index 6533aea10..d68d941f7 100644 --- a/dev/DevelopmentAdmin.php +++ b/dev/DevelopmentAdmin.php @@ -1,10 +1,15 @@ 'index', 'build/defaults' => 'buildDefaults', - '$Action' => '$Action', - '$Action//$Action/$ID' => 'handleAction', + 'generatesecuretoken' => 'generatesecuretoken', + '$Action' => 'runRegisteredController', ); private static $allowed_actions = array( 'index', - 'tests', - 'jstests', - 'tasks', - 'viewmodel', - 'build', - 'reset', - 'viewcode', - 'generatesecuretoken', 'buildDefaults', + 'runRegisteredController', + 'generatesecuretoken', ); + public function init() { parent::init(); @@ -80,20 +80,8 @@ class DevelopmentAdmin extends Controller { } public function index() { - $actions = array( - "build" => "Build/rebuild this environment. Call this whenever you have updated your project sources", - "tests" => "See a list of unit tests to run", - "tests/all" => "Run all tests", - "jstests" => "See a list of JavaScript tests to run", - "jstests/all" => "Run all JavaScript tests", - "tasks" => "See a list of build tasks to run" - ); - // Web mode if(!Director::is_cli()) { - // This action is sake-only right now. - unset($actions["modules/add"]); - $renderer = DebugView::create(); $renderer->writeHeader(); $renderer->writeInfo("SilverStripe Development Tools", Director::absoluteBaseURL()); @@ -101,7 +89,7 @@ class DevelopmentAdmin extends Controller { echo '