Merge remote-tracking branch 'origin/3.1' into 3

Conflicts:
	docs/en/02_Developer_Guides/09_Security/04_Secure_Coding.md
	docs/en/05_Contributing/01_Code.md
	forms/TreeDropdownField.php
	model/DataObject.php
	security/Member.php
	tests/model/DataObjectTest.php
This commit is contained in:
Damian Mooyman 2015-03-11 11:40:06 +13:00
commit 319b96b48b
92 changed files with 1310 additions and 325 deletions

View File

@ -10,7 +10,7 @@ jQuery.noConflict();
// Entwine's 'fromWindow::onresize' does not trigger on IE8. Use synthetic event.
var cb = function() {$('.cms-container').trigger('windowresize');};
// Workaround to avoid IE8 infinite loops when elements are resized as a result of this event
// Workaround to avoid IE8 infinite loops when elements are resized as a result of this event
if($.browser.msie && parseInt($.browser.version, 10) < 9) {
var newWindowWidth = $(window).width(), newWindowHeight = $(window).height();
if(newWindowWidth != windowWidth || newWindowHeight != windowHeight) {
@ -26,7 +26,7 @@ jQuery.noConflict();
// setup jquery.entwine
$.entwine.warningLevel = $.entwine.WARN_LEVEL_BESTPRACTISE;
$.entwine('ss', function($) {
/*
* Handle messages sent via nested iframes
* Messages should be raised via postMessage with an object with the 'type' parameter given.
@ -35,20 +35,20 @@ jQuery.noConflict();
* type should be one of:
* - 'event' - Will trigger the given event (specified by 'event') on the target
* - 'callback' - Will call the given method (specified by 'callback') on the target
*/
*/
$(window).on("message", function(e) {
var target,
event = e.originalEvent,
data = JSON.parse(event.data);
// Reject messages outside of the same origin
if($.path.parseUrl(window.location.href).domain !== $.path.parseUrl(event.origin).domain) return;
// Get target of this action
target = typeof(data.target) === 'undefined'
? $(window)
: $(data.target);
// Determine action
switch(data.type) {
case 'event':
@ -59,18 +59,18 @@ jQuery.noConflict();
break;
}
});
/**
* Position the loading spinner animation below the ss logo
*/
*/
var positionLoadingSpinner = function() {
var offset = 120; // offset from the ss logo
var spinner = $('.ss-loading-screen .loading-animation');
var spinner = $('.ss-loading-screen .loading-animation');
var top = ($(window).height() - spinner.height()) / 2;
spinner.css('top', top + offset);
spinner.show();
};
// apply an select element only when it is ready, ie. when it is rendered into a template
// with css applied and got a width value.
var applyChosen = function(el) {
@ -89,13 +89,13 @@ jQuery.noConflict();
setTimeout(function() {
// Make sure it's visible before applying the ui
el.show();
applyChosen(el); },
applyChosen(el); },
500);
}
};
/**
* Compare URLs, but normalize trailing slashes in
* Compare URLs, but normalize trailing slashes in
* URL to work around routing weirdnesses in SS_HTTPRequest.
* Also normalizes relative URLs by prefixing them with the <base>.
*/
@ -105,11 +105,11 @@ jQuery.noConflict();
url2 = $.path.isAbsoluteUrl(url2) ? url2 : $.path.makeUrlAbsolute(url2, baseUrl);
var url1parts = $.path.parseUrl(url1), url2parts = $.path.parseUrl(url2);
return (
url1parts.pathname.replace(/\/*$/, '') == url2parts.pathname.replace(/\/*$/, '') &&
url1parts.pathname.replace(/\/*$/, '') == url2parts.pathname.replace(/\/*$/, '') &&
url1parts.search == url2parts.search
);
};
$(window).bind('resize', positionLoadingSpinner).trigger('resize');
// global ajax handlers
@ -142,7 +142,7 @@ jQuery.noConflict();
reathenticate = xhr.getResponseHeader('X-Reauthenticate'),
msgType = (xhr.status < 200 || xhr.status > 399) ? 'bad' : 'good',
ignoredMessages = ['OK'];
// Enable reauthenticate dialog if requested
if(reathenticate) {
$('.cms-container').showLoginDialog();
@ -158,14 +158,14 @@ jQuery.noConflict();
/**
* Main LeftAndMain interface with some control panel and an edit form.
*
*
* Events:
* ajaxsubmit - ...
* validate - ...
* aftersubmitform - ...
*/
$('.cms-container').entwine({
/**
* Tracks current panel request.
*/
@ -177,7 +177,7 @@ jQuery.noConflict();
FragmentXHR: {},
StateChangeCount: 0,
/**
* Options for the threeColumnCompressor layout algorithm.
*
@ -198,7 +198,7 @@ jQuery.noConflict();
// Browser detection
if($.browser.msie && parseInt($.browser.version, 10) < 8) {
$('.ss-loading-screen').append(
'<p class="ss-loading-incompat-warning"><span class="notice">' +
'<p class="ss-loading-incompat-warning"><span class="notice">' +
'Your browser is not compatible with the CMS interface. Please use Internet Explorer 8+, Google Chrome or Mozilla Firefox.' +
'</span></p>'
).css('z-index', $('.ss-loading-screen').css('z-index')+1);
@ -207,7 +207,7 @@ jQuery.noConflict();
this._super();
return;
}
// Initialize layouts
this.redraw();
@ -216,7 +216,7 @@ jQuery.noConflict();
$('body').removeClass('loading');
$(window).unbind('resize', positionLoadingSpinner);
this.restoreTabState();
this._super();
},
@ -326,9 +326,9 @@ jQuery.noConflict();
* Proxy around History.pushState() which handles non-HTML5 fallbacks,
* as well as global change tracking. Change tracking needs to be synchronous rather than event/callback
* based because the user needs to be able to abort the action completely.
*
*
* See handleStateChange() for more details.
*
*
* Parameters:
* - {String} url
* - {String} title New window title
@ -343,20 +343,20 @@ jQuery.noConflict();
// Check change tracking (can't use events as we need a way to cancel the current state change)
var contentEls = this._findFragments(data.pjax ? data.pjax.split(',') : ['Content']);
var trackedEls = contentEls.find(':data(changetracker)').add(contentEls.filter(':data(changetracker)'));
if(trackedEls.length) {
var abort = false;
trackedEls.each(function() {
if(!$(this).confirmUnsavedChanges()) abort = true;
});
if(abort) return;
}
// Save tab selections so we can restore them later
this.saveTabState();
if(window.History.enabled) {
$.extend(data, {__forceReferer: forceReferer});
// Active menu item is set based on X-Controller ajax header,
@ -382,31 +382,31 @@ jQuery.noConflict();
/**
* Function: submitForm
*
*
* Parameters:
* {DOMElement} form - The form to be submitted. Needs to be passed
* in to avoid entwine methods/context being removed through replacing the node itself.
* {DOMElement} button - The pressed button (optional)
* {Function} callback - Called in complete() handler of jQuery.ajax()
* {Object} ajaxOptions - Object literal to merge into $.ajax() call
*
*
* Returns:
* (boolean)
*/
submitForm: function(form, button, callback, ajaxOptions) {
var self = this;
// look for save button
if(!button) button = this.find('.Actions :submit[name=action_save]');
// default to first button if none given - simulates browser behaviour
if(!button) button = this.find('.Actions :submit:first');
form.trigger('beforesubmitform');
this.trigger('submitform', {form: form, button: button});
// set button to "submitting" state
$(button).addClass('loading');
// validate if required
var validationResult = form.validate();
if(typeof validationResult!=='undefined' && !validationResult) {
@ -417,12 +417,12 @@ jQuery.noConflict();
return false;
}
// get all data from the form
var formData = form.serializeArray();
// add button action
formData.push({name: $(button).attr('name'), value:'1'});
// Artificial HTTP referer, IE doesn't submit them via ajax.
// Artificial HTTP referer, IE doesn't submit them via ajax.
// Also rewrites anchors to their page counterparts, which is important
// as automatic browser ajax response redirects seem to discard the hash/fragment.
// TODO Replaces trailing slashes added by History after locale (e.g. admin/?locale=en/)
@ -437,7 +437,7 @@ jQuery.noConflict();
// sending back different `X-Pjax` headers and content
jQuery.ajax(jQuery.extend({
headers: {"X-Pjax" : "CurrentForm,Breadcrumbs"},
url: form.attr('action'),
url: form.attr('action'),
data: formData,
type: 'POST',
complete: function() {
@ -453,7 +453,7 @@ jQuery.noConflict();
newContentEls.filter('form').trigger('aftersubmitform', {status: status, xhr: xhr, formData: formData});
}
}, ajaxOptions));
return false;
},
@ -462,21 +462,21 @@ jQuery.noConflict();
* To trigger loading, pass a new URL to window.History.pushState().
* Use loadPanel() as a pushState() wrapper as it provides some additional functionality
* like global changetracking and user aborts.
*
*
* Due to the nature of history management, no callbacks are allowed.
* Use the 'beforestatechange' and 'afterstatechange' events instead,
* or overwrite the beforeLoad() and afterLoad() methods on the
* or overwrite the beforeLoad() and afterLoad() methods on the
* DOM element you're loading the new content into.
* Although you can pass data into pushState(), it shouldn't contain
* Although you can pass data into pushState(), it shouldn't contain
* DOM elements or callback closures.
*
*
* The passed URL should allow reconstructing important interface state
* without additional parameters, in the following use cases:
* - Explicit loading through History.pushState()
* - Implicit loading through browser navigation event triggered by the user (forward or back)
* - Full window refresh without ajax
* For example, a ModelAdmin search event should contain the search terms
* as URL parameters, and the result display should automatically appear
* as URL parameters, and the result display should automatically appear
* if the URL is loaded without ajax.
*/
handleStateChange: function() {
@ -502,22 +502,22 @@ jQuery.noConflict();
// that can be reloaded without reloading the whole window.
if(contentEls.length < fragmentsArr.length) {
fragments = 'Content', fragmentsArr = ['Content'];
contentEls = this._findFragments(fragmentsArr);
contentEls = this._findFragments(fragmentsArr);
}
this.trigger('beforestatechange', {state: state, element: contentEls});
// Set Pjax headers, which can declare a preference for the returned view.
// The actually returned view isn't always decided upon when the request
// is fired, so the server might decide to change it based on its own logic.
headers['X-Pjax'] = fragments;
// Set 'fake' referer - we call pushState() before making the AJAX request, so we have to
// set our own referer here
if (typeof state.data.__forceReferer !== 'undefined') {
// Ensure query string is properly encoded if present
var url = state.data.__forceReferer;
try {
// Prevent double-encoding by attempting to decode
url = decodeURI(url);
@ -528,7 +528,7 @@ jQuery.noConflict();
headers['X-Backurl'] = encodeURI(url);
}
}
contentEls.addClass('loading');
var xhr = $.ajax({
headers: headers,
@ -543,7 +543,7 @@ jQuery.noConflict();
self.trigger('afterstatechange', {data: data, status: status, xhr: xhr, element: els, state: state});
}
});
this.setStateChangeXHR(xhr);
},
@ -633,7 +633,7 @@ jQuery.noConflict();
// Support a full reload
if(xhr.getResponseHeader('X-Reload') && xhr.getResponseHeader('X-ControllerURL')) {
document.location.href = $('base').attr('href').replace(/\/*$/, '')
document.location.href = $('base').attr('href').replace(/\/*$/, '')
+ '/' + xhr.getResponseHeader('X-ControllerURL');
return;
}
@ -651,7 +651,7 @@ jQuery.noConflict();
if(xhr.getResponseHeader('Content-Type').match(/^((text)|(application))\/json[ \t]*;?/i)) {
newFragments = data;
} else {
// Fall back to replacing the content fragment if HTML is returned
var fragment = document.createDocumentFragment();
jQuery.clean( [ data ], document, fragment, [] );
@ -732,9 +732,9 @@ jQuery.noConflict();
},
/**
*
*
* Parameters:
*
*
* Parameters:
* - fragments {Array}
* Returns: jQuery collection
*/
@ -751,14 +751,14 @@ jQuery.noConflict();
/**
* Function: refresh
*
*
* Updates the container based on the current url
*
* Returns: void
*/
refresh: function() {
$(window).trigger('statechange');
$(this).redraw();
},
@ -787,7 +787,7 @@ jQuery.noConflict();
window.sessionStorage.setItem(tabsUrl, JSON.stringify(selectedTabs));
} catch(err) {
if (err.code === DOMException.QUOTA_EXCEEDED_ERR && window.sessionStorage.length === 0) {
// If this fails we ignore the error as the only issue is that it
// If this fails we ignore the error as the only issue is that it
// does not remember the tab state.
// This is a Safari bug which happens when private browsing is enabled.
return;
@ -856,7 +856,7 @@ jQuery.noConflict();
var s = window.sessionStorage;
if(url) {
s.removeItem('tabs-' + url);
s.removeItem('tabs-' + url);
} else {
for(var i=0;i<s.length;i++) {
if(s.key(i).match(/^tabs-/)) s.removeItem(s.key(i));
@ -877,7 +877,7 @@ jQuery.noConflict();
.replace(/#.*/, '')
.replace($('base').attr('href'), '');
},
showLoginDialog: function() {
var tempid = $('body').data('member-tempid'),
dialog = $('.leftandmain-logindialog'),
@ -885,13 +885,13 @@ jQuery.noConflict();
// Force regeneration of any existing dialog
if(dialog.length) dialog.remove();
// Join url params
url = $.path.addSearchParams(url, {
'tempid': tempid,
'BackURL': window.location.href
});
// Show a placeholder for instant feedback. Will be replaced with actual
// form dialog once its loaded.
dialog = $('<div class="leftandmain-logindialog"></div>');
@ -900,12 +900,12 @@ jQuery.noConflict();
$('body').append(dialog);
}
});
// Login dialog page
$('.leftandmain-logindialog').entwine({
onmatch: function() {
this._super();
// Create jQuery dialog
this.ssdialog({
iframeUrl: this.data('url'),
@ -921,7 +921,7 @@ jQuery.noConflict();
},
close: function() {
$('.ui-widget-overlay').removeClass('leftandmain-logindialog-overlay');
}
}
});
},
onunmatch: function() {
@ -952,7 +952,7 @@ jQuery.noConflict();
this.close();
}
});
/**
* Add loading overlay to selected regions in the CMS automatically.
* Not applied to all "*.loading" elements to avoid secondary regions
@ -997,7 +997,7 @@ jQuery.noConflict();
return;
}
var href = this.attr('href'),
var href = this.attr('href'),
url = (href && !href.match(/^#/)) ? href : this.data('href'),
data = {pjax: this.data('pjaxTarget')};
@ -1015,17 +1015,17 @@ jQuery.noConflict();
onclick: function(e) {
$(this).removeClass('ui-button-text-only');
$(this).addClass('ss-ui-button-loading ui-button-text-icons');
var loading = $(this).find(".ss-ui-loading-icon");
if(loading.length < 1) {
loading = $("<span></span>").addClass('ss-ui-loading-icon ui-button-icon-primary ui-icon');
$(this).prepend(loading);
}
loading.show();
var href = this.attr('href'), url = href ? href : this.data('href');
jQuery.ajax({
@ -1033,16 +1033,16 @@ jQuery.noConflict();
// Ensure that form view is loaded (rather than whole "Content" template)
complete: function(xmlhttp, status) {
var msg = (xmlhttp.getResponseHeader('X-Status')) ? xmlhttp.getResponseHeader('X-Status') : xmlhttp.responseText;
try {
if (typeof msg != "undefined" && msg !== null) eval(msg);
}
catch(e) {}
loading.hide();
$(".cms-container").refresh();
$(this).removeClass('ss-ui-button-loading ui-button-text-icons');
$(this).addClass('ui-button-text-only');
},
@ -1073,14 +1073,14 @@ jQuery.noConflict();
dialog = $('<div class="ss-ui-dialog" id="' + id + '" />');
$('body').append(dialog);
}
var extraClass = this.data('popupclass')?this.data('popupclass'):'';
dialog.ssdialog({iframeUrl: this.attr('href'), autoOpen: true, dialogExtraClass: extraClass});
return false;
}
});
/**
* Add styling to all contained buttons, and create buttonsets if required.
*/
@ -1110,20 +1110,20 @@ jQuery.noConflict();
if(window.debug) console.log('redraw', this.attr('class'), this.get(0));
// Remove whitespace to avoid gaps with inline elements
this.contents().filter(function() {
return (this.nodeType == 3 && !/\S/.test(this.nodeValue));
this.contents().filter(function() {
return (this.nodeType == 3 && !/\S/.test(this.nodeValue));
}).remove();
// Init buttons if required
this.find('.ss-ui-button').each(function() {
if(!$(this).data('button')) $(this).button();
});
// Mark up buttonsets
this.find('.ss-ui-buttonset').buttonset();
}
});
/**
* Duplicates functionality in DateField.js, but due to using entwine we can match
* the DOM element on creation, rather than onclick - which allows us to decorate
@ -1145,23 +1145,23 @@ jQuery.noConflict();
$(this).datepicker(config);
// // Unfortunately jQuery UI only allows configuration of icon images, not sprites
// this.next('button').button('option', 'icons', {primary : 'ui-icon-calendar'});
this._super();
},
onunmatch: function() {
this._super();
}
});
/**
* Styled dropdown select fields via chosen. Allows things like search and optgroup
* selection support. Rather than manually adding classes to selects we want
* selection support. Rather than manually adding classes to selects we want
* styled, we style everything but the ones we tell it not to.
*
* For the CMS we also need to tell the parent div that his has a select so
* For the CMS we also need to tell the parent div that it has a select so
* we can fix the height cropping.
*/
$('.cms .field.dropdown select, .cms .field select[multiple], .fieldholder-small select.dropdown').entwine({
onmatch: function() {
if(this.is('.no-chzn')) {
@ -1178,20 +1178,20 @@ jQuery.noConflict();
// Apply Chosen
applyChosen(this);
this._super();
},
onunmatch: function() {
this._super();
}
});
$(".cms-panel-layout").entwine({
redraw: function() {
if(window.debug) console.log('redraw', this.attr('class'), this.get(0));
}
});
/**
* Overload the default GridField behaviour (open a new URL in the browser)
* with the CMS-specific ajax loading.
@ -1209,7 +1209,7 @@ jQuery.noConflict();
/**
* Generic search form in the CMS, often hooked up to a GridField results display.
*/
*/
$('.cms-search-form').entwine({
onsubmit: function(e) {
// Remove empty elements and make the URL prettier
@ -1267,7 +1267,7 @@ jQuery.noConflict();
},
onremove: function() {
if(window.debug) console.log('saving', this.data('url'), this);
// Save the HTML state at the last possible moment.
// Don't store the DOM to avoid memory leaks.
if(!this.data('deferredNoCache')) window._panelDeferredCache[this.data('url')] = this.html();
@ -1326,7 +1326,7 @@ jQuery.noConflict();
if(!this.data('uiTabs')) this.tabs({
active: (activeTab.index() != -1) ? activeTab.index() : 0,
beforeLoad: function(e, ui) {
// Disable automatic ajax loading of tabs without matching DOM elements,
// Disable automatic ajax loading of tabs without matching DOM elements,
// determining if the current URL differs from the tab URL is too error prone.
return false;
},
@ -1347,7 +1347,7 @@ jQuery.noConflict();
}
});
},
/**
* Ensure hash links are prefixed with the current page URL,
* otherwise jQuery interprets them as being external.
@ -1362,7 +1362,7 @@ jQuery.noConflict();
}
});
});
}(jQuery));
var statusMessage = function(text, type) {

View File

@ -0,0 +1,16 @@
// This file was generated by GenerateJavaScriptI18nTask from javascript/lang/src/eo.js.
// See https://github.com/silverstripe/silverstripe-buildtools for details
if(typeof(ss) == 'undefined' || typeof(ss.i18n) == 'undefined') {
if(typeof(console) != 'undefined') console.error('Class ss.i18n not defined');
} else {
ss.i18n.addDictionary('eo', {
"LeftAndMain.CONFIRMUNSAVED": "Ĉu vi vere volas navigi for de ĉi tiu paĝo?\n\nAVERTO: Viaj ŝanĝoj ne estas konservitaj.\n\nPremu je Akcepti por daŭrigi, aŭ Nuligi por resti ĉe la aktuala paĝo.",
"LeftAndMain.CONFIRMUNSAVEDSHORT": "AVERTO: Viaj ŝanĝoj ne estas konservitaj.",
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Ĉu vi vere volas forigi %s grupojn?",
"ModelAdmin.SAVED": "Konservita",
"ModelAdmin.REALLYDELETE": "Ĉi vi vere volas forigi?",
"ModelAdmin.DELETED": "Forigita",
"ModelAdmin.VALIDATIONERROR": "Validiga eraro",
"LeftAndMain.PAGEWASDELETED": "Ĉi tiu paĝo estas forigita. Por redakti paĝon, elektu ĝin maldekstre."
});
}

View File

@ -0,0 +1,16 @@
// This file was generated by GenerateJavaScriptI18nTask from javascript/lang/src/lt.js.
// See https://github.com/silverstripe/silverstripe-buildtools for details
if(typeof(ss) == 'undefined' || typeof(ss.i18n) == 'undefined') {
if(typeof(console) != 'undefined') console.error('Class ss.i18n not defined');
} else {
ss.i18n.addDictionary('lt', {
"LeftAndMain.CONFIRMUNSAVED": "Ar tikrai norite išeiti iš šio puslapio?\n\nDĖMESIO: Jūsų pakeitimai neišsaugoti.\n\nNorėdami tęsti, spauskite OK, jeigu norite likti, spauskite Cancel.",
"LeftAndMain.CONFIRMUNSAVEDSHORT": "DĖMESIO: Jūsų pakeitimai neišsaugoti.",
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Ar tikrai norite ištrinti %s grupes?",
"ModelAdmin.SAVED": "Išsaugota",
"ModelAdmin.REALLYDELETE": "Ar tikrai norite ištrinti?",
"ModelAdmin.DELETED": "Ištrinta",
"ModelAdmin.VALIDATIONERROR": "Tikrinimo klaida",
"LeftAndMain.PAGEWASDELETED": "Šis puslapis ištrintas. Norėdami redaguoti puslapį, pasirinkite jį kairėje."
});
}

View File

@ -0,0 +1,16 @@
// This file was generated by GenerateJavaScriptI18nTask from javascript/lang/src/nb.js.
// See https://github.com/silverstripe/silverstripe-buildtools for details
if(typeof(ss) == 'undefined' || typeof(ss.i18n) == 'undefined') {
if(typeof(console) != 'undefined') console.error('Class ss.i18n not defined');
} else {
ss.i18n.addDictionary('nb', {
"LeftAndMain.CONFIRMUNSAVED": "Er du sikker på at du vil forlate denne siden?\n\nADVARSEL: Endringene din har ikke blitt lagret.\n\nTrykk OK for å fortsette eller Avbryt for å holde deg på samme side.",
"LeftAndMain.CONFIRMUNSAVEDSHORT": "ADVARSEL: Endringene dine har ikke blitt lagret.",
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Vil du virkelig slette %s grupper?",
"ModelAdmin.SAVED": "Lagret",
"ModelAdmin.REALLYDELETE": "Vil du virkelig slette?",
"ModelAdmin.DELETED": "Slettet",
"ModelAdmin.VALIDATIONERROR": "Valideringsfeil",
"LeftAndMain.PAGEWASDELETED": "Denne siden ble slettet. For å redigere en side, velg den fra listen til venstre."
});
}

View File

@ -10,7 +10,7 @@ if(typeof(ss) == 'undefined' || typeof(ss.i18n) == 'undefined') {
"ModelAdmin.SAVED": "Opgeslagen",
"ModelAdmin.REALLYDELETE": "Weet u zeker dat u wilt verwijderen?",
"ModelAdmin.DELETED": "Verwijderd",
"ModelAdmin.VALIDATIONERROR": "Validatie fout",
"LeftAndMain.PAGEWASDELETED": "Deze pagina is verwijderd. Om een pagina aan te passen, selecteer pagina aan de linkerkant."
"ModelAdmin.VALIDATIONERROR": "Validatiefout",
"LeftAndMain.PAGEWASDELETED": "Deze pagina is verwijderd. Om een pagina aan te passen, selecteer deze aan de linkerkant."
});
}

View File

@ -0,0 +1,16 @@
// This file was generated by GenerateJavaScriptI18nTask from javascript/lang/src/sl.js.
// See https://github.com/silverstripe/silverstripe-buildtools for details
if(typeof(ss) == 'undefined' || typeof(ss.i18n) == 'undefined') {
if(typeof(console) != 'undefined') console.error('Class ss.i18n not defined');
} else {
ss.i18n.addDictionary('sl', {
"LeftAndMain.CONFIRMUNSAVED": "Res želite zapusitit stran?\n\nOPOZORILO: spremembe niso bile shranjene\n\nKliknite OK za nadaljevanje ali Prekliči, da ostanete na trenutni strani.",
"LeftAndMain.CONFIRMUNSAVEDSHORT": "OPOZORILO: spremembe niso bile shranjene.",
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Izbrišem %s skupin?",
"ModelAdmin.SAVED": "Shranjeno",
"ModelAdmin.REALLYDELETE": "Izbrišem?",
"ModelAdmin.DELETED": "Izbrisano",
"ModelAdmin.VALIDATIONERROR": "Napaka pri preverjanju",
"LeftAndMain.PAGEWASDELETED": "Stran je bila izbrisana. Za urejanje izberite stran na levi."
});
}

View File

@ -0,0 +1,10 @@
{
"LeftAndMain.CONFIRMUNSAVED": "Är du säker på att du vill lämna denna sida?\n\nVARNING: Dina ändringar har inte sparats.\n\nTryck OK för att lämna sidan eller Avbryt för att stanna på aktuell sida.",
"LeftAndMain.CONFIRMUNSAVEDSHORT": "WARNING: Your changes have not been saved.",
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Vill du verkligen radera %s grupper?",
"ModelAdmin.SAVED": "Sparad",
"ModelAdmin.REALLYDELETE": "Vill du verkligen radera?",
"ModelAdmin.DELETED": "Raderad",
"ModelAdmin.VALIDATIONERROR": "Valideringsfel",
"LeftAndMain.PAGEWASDELETED": "Sidan raderades. För att redigera en sida, välj den från menyn till vänster."
}

View File

@ -0,0 +1,16 @@
// This file was generated by GenerateJavaScriptI18nTask from javascript/lang/src/sv.js.
// See https://github.com/silverstripe/silverstripe-buildtools for details
if(typeof(ss) == 'undefined' || typeof(ss.i18n) == 'undefined') {
if(typeof(console) != 'undefined') console.error('Class ss.i18n not defined');
} else {
ss.i18n.addDictionary('sv', {
"LeftAndMain.CONFIRMUNSAVED": "Är du säker på att du vill lämna denna sida?\n\nVARNING: Dina ändringar har inte sparats.\n\nTryck OK för att lämna sidan eller Avbryt för att stanna på aktuell sida.",
"LeftAndMain.CONFIRMUNSAVEDSHORT": "WARNING: Your changes have not been saved.",
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Vill du verkligen radera %s grupper?",
"ModelAdmin.SAVED": "Sparad",
"ModelAdmin.REALLYDELETE": "Vill du verkligen radera?",
"ModelAdmin.DELETED": "Raderad",
"ModelAdmin.VALIDATIONERROR": "Valideringsfel",
"LeftAndMain.PAGEWASDELETED": "Sidan raderades. För att redigera en sida, välj den från menyn till vänster."
});
}

View File

@ -85,8 +85,6 @@ require_once 'control/injector/Injector.php';
// Initialise the dependency injector as soon as possible, as it is
// subsequently used by some of the following code
$injector = new Injector(array('locator' => 'SilverStripeServiceConfigurationLocator'));
$injector->registerService(Config::inst());
Injector::set_inst($injector);
///////////////////////////////////////////////////////////////////////////////

View File

@ -113,7 +113,7 @@ Used in side panels and action tabs
.cms table.ss-gridfield-table tr th input.ss-gridfield-sort:-ms-input-placeholder { font-style: italic; color: #ced5d7; }
.cms table.ss-gridfield-table tr th input.ss-gridfield-sort:placeholder { font-style: italic; color: #ced5d7; }
.cms table.ss-gridfield-table tr th input.ss-gridfield-sort:focus { -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; }
.cms table.ss-gridfield-table tr th span.non-sortable { display: block; }
.cms table.ss-gridfield-table tr th span.non-sortable { display: block; padding: 6px 8px; }
.cms table.ss-gridfield-table tr td { border-right: 1px solid rgba(0, 0, 0, 0.1); padding: 8px 8px; color: #666; }
.cms table.ss-gridfield-table tr td.bottom-all { -moz-border-radius-bottomleft: 5px; -webkit-border-bottom-left-radius: 5px; border-bottom-left-radius: 5px; -moz-border-radius-bottomright: 5px; -webkit-border-bottom-right-radius: 5px; border-bottom-right-radius: 5px; background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2IwYmVjNyIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzk4YWFiNiIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background-size: 100%; background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #b0bec7), color-stop(100%, #98aab6)); background-image: -moz-linear-gradient(#b0bec7, #98aab6); background-image: -webkit-linear-gradient(#b0bec7, #98aab6); background-image: linear-gradient(#b0bec7, #98aab6); padding: 4px 12px; }
.cms table.ss-gridfield-table tr td.bottom-all .datagrid-footer-message { text-align: center; padding-top: 6px; color: white; }

View File

@ -11,6 +11,7 @@
Used in side panels and action tabs
*/
.ss-uploadfield .clear { clear: both; }
.ss-uploadfield .description { margin-left: 0; }
.ss-uploadfield .middleColumn { min-width: 510px; max-width: 600px; width: 100%; margin-left: 0; clear: both; padding: 0; background: #fff; border: 1px solid #b3b3b3; -moz-border-radius: 4px; -webkit-border-radius: 4px; border-radius: 4px; background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2VmZWZlZiIvPjxzdG9wIG9mZnNldD0iMTAlIiBzdG9wLWNvbG9yPSIjZmZmZmZmIi8+PHN0b3Agb2Zmc2V0PSI5MCUiIHN0b3AtY29sb3I9IiNmZmZmZmYiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiNlZmVmZWYiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2dyYWQpIiAvPjwvc3ZnPiA='); background-size: 100%; background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #efefef), color-stop(10%, #ffffff), color-stop(90%, #ffffff), color-stop(100%, #efefef)); background-image: -moz-linear-gradient(#efefef, #ffffff 10%, #ffffff 90%, #efefef); background-image: -webkit-linear-gradient(#efefef, #ffffff 10%, #ffffff 90%, #efefef); background-image: linear-gradient(#efefef, #ffffff 10%, #ffffff 90%, #efefef); }
.ss-uploadfield .ss-uploadfield-item { margin: 0; padding: 15px; overflow: auto; }
.ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-preview { height: 60px; line-height: 60px; width: 80px; text-align: center; font-weight: bold; float: left; overflow: hidden; }

View File

@ -12,7 +12,7 @@
* $this->get("your/url");
*
* // Submit a form on the page that you get in response
* $this->submitForm("MyForm_ID", array("Email" => "invalid email ^&*&^"));
* $this->submitForm("MyForm_ID", "action_dologin", array("Email" => "invalid email ^&*&^"));
*
* // Validate the content that is returned
* $this->assertExactMatchBySelector("#MyForm_ID p.error", array("That email address is invalid."));

View File

@ -4,7 +4,7 @@ SilverStripe CMS needs to be installed on a web server. Content authors and webs
to access a web-based GUI to do their day-to-day work. Website designers and developers require access to the files on
the server to update templates, website logic, and perform upgrades or maintenance.
Our web-based [PHP installer](/installation) can check if you meet the requirements listed below.
Our web-based [PHP installer](installation/) can check if you meet the requirements listed below.
## Web server software requirements

View File

@ -2,7 +2,7 @@
SilverStripe should be able to be installed on any Linux, Unix or *nix like OS as long as the correct server software is installed and configured (referred to a *nix in this document from herein). It is common that web hosting that you may use for your production SilverStripe application will be *nix based, here you may also want to use *nix locally to ensure how you develop locally mimics closely your production environment.
Is important to ensure you check the [Server Requirements](/Getting_Started/Installation/Server_Requirements) list before acquiring and installing SilverStripe on your *nix server (locally or otherwise).
Is important to ensure you check the [Server Requirements](/getting_started/server_requirements) list before acquiring and installing SilverStripe on your *nix server (locally or otherwise).
At a high level you will need a:
* Web server e.g. Apache, Nginx
@ -12,7 +12,7 @@ At a high level you will need a:
##*nix installation guides on the web
There are a number of good step by step guides covering server setups and installing of SilverStripe on the various flavours of *nix systems.
Note: Many of the following guides simply download SilverStripe as a zipped file. We recommend the use of [Composer](/Getting_Started/Composer/) once you get to the point of installing SilverStripe (though the choice is up to you). Always ensure you get the latest version if you are starting a new project.
Note: Many of the following guides simply download SilverStripe as a zipped file. We recommend the use of [Composer](/getting_started/composer/) once you get to the point of installing SilverStripe (though the choice is up to you). Always ensure you get the latest version if you are starting a new project.
###Known (but not exhaustive) list
* [How To Install Silverstripe on Your VPS](https://www.digitalocean.com/community/tutorials/how-to-install-silverstripe-on-your-vps)

View File

@ -5,7 +5,7 @@ done through [WampServer](http://www.wampserver.com/en/). This can be useful if
want a Microsoft Windows machine with a very similar environment.
Note: Installing on Microsoft's IIS webserver through Microsoft WebPI is likely to be easier, see
[installation-on-windows-pi](windows-pi).
[Windows with Web Platform Installer](other_installation_options/windows_platform_installer).
## Install WAMP

View File

@ -73,11 +73,11 @@ every page on the site, if that's easier.
Please make sure all code inside `*.php` files is wrapped in classes. Due to the way `[api:ManifestBuilder]`
includes all files with this extension, any **procedural code will be executed on every call**. The most common error here
is putting a test.php/phpinfo.php file in the document root. See [datamodel](/topics/datamodel) and [controllers](/topics/controller)
is putting a test.php/phpinfo.php file in the document root. See [datamodel](/developer_guides/data_model_and_orm) and [controllers](/developer_guides/controllers)
for ways how to structure your code.
Also, please check that you have PHP enabled on the webserver, and you're running PHP 5.1 or later.
The web-based [SilverStripe installer](/installation) can help you with this.
The web-based [SilverStripe installer](/getting_started/installation) can help you with this.
## I've got file permission problems during installation

View File

@ -107,4 +107,4 @@ e.g. `/etc/nginx/sites-enabled/mysite`:
include /etc/nginx/silverstripe.conf;
}
For more information on nginx configuration, please see the [nginx installation](nginx) page.
For more information on nginx configuration, please see the [nginx installation](configure_nginx) page.

View File

@ -3,12 +3,12 @@
These instructions show you how to install SilverStripe on any web server.
The best way to install from the source code is to use [Composer](../composer).
Check out our operating system specific guides for [Linux](linux_unix),
[Windows Server](windows-pi) and [Mac OSX](mac-osx).
[Windows Server](windows) and [Mac OSX](mac_osx).
## Installation Steps
* [Download](http://silverstripe.org/download) the installer package
* Make sure the webserver has MySQL and PHP support. See [Server Requirements](server-requirements) for more information.
* Make sure the webserver has MySQL and PHP support. See [Server Requirements](../server_requirements) for more information.
* Unpack the installer somewhere into your web-root. Usually the www folder or similar. Most downloads from SilverStripe
are compressed tarballs. To extract these files you can either do them natively (Unix) or with 7-Zip (Windows)
* Visit your sites domain or IP address in your web browser.
@ -18,7 +18,7 @@ name' and the default login details. Follow the questions and select the *instal
## Issues?
If the above steps don't work for any reason have a read of the [Common Problems](common-problems) section.
If the above steps don't work for any reason have a read of the [Common Problems](common_problems) section.
<div class="notice" markdown="1">
SilverStripe ships with default rewriting rules specific to your web server. Apart from

View File

@ -3,7 +3,7 @@
Composer is a package management tool for PHP that lets you install and upgrade SilverStripe and its modules. Although installing Composer is one extra step, it will give you much more flexibility than just downloading the file from silverstripe.org. This is our recommended way of downloading SilverStripe and managing your code.
For more information about Composer, visit [its website](http://getcomposer.org/).
We also have separate instructions for [installing modules with Composer](/topics/modules).
We also have separate instructions for [installing modules with Composer](/developer_guides/extending/modules).
# Basic usage
@ -36,7 +36,7 @@ If you already have composer installed you can update it by running:
Composer updates regularly, so you should run this command fairly often. These instructions assume you are running the latest version.
## Installing Composer on Windows WAMP
For those that use WAMP as a development environment, [detailed information is available on installing using Composer.](/installation/windows-wamp#install-wamp)
For those that use WAMP as a development environment, [detailed information is available on installing using Composer.](/getting_started/installation/windows)
## Create a new site
@ -108,6 +108,70 @@ So, your deployment process, as it relates to Composer, should be as follows:
* Deploy your project code base, using the deployment tool of your choice.
* Run `composer install --no-dev -o` on your production version.
## Composer managed modules, Git and .gitignore
Modules and themes managed by composer should not be committed with your projects source code. For more details read [Should I commit the dependencies in my vendor directory?](https://getcomposer.org/doc/faqs/should-i-commit-the-dependencies-in-my-vendor-directory.md).
Since SilverStripe modules are installed in to thier own folder, you have to manage your [.gitignore](http://git-scm.com/docs/gitignore) to ensure they are ignored from your repository.
Here is the default SilverStripe [.gitignore](http://git-scm.com/docs/gitignore) with the forum module ignored
assets/*
_ss_environment.php
tools/phing-metadata
silverstripe-cache
.buildpath
.project
.settings
.idea
.DS_Store
vendor/
# Don't include the forum module, as this will be installed with composer
forum
In large projects it can get difficult to manage your [.gitignore](http://git-scm.com/docs/gitignore) and ensure it contains all composer managed modules and themes.
You can automate this with the [SSAutoGitIgnore](https://github.com/guru-digital/SSAutoGitIgnore/) package.
This package will maintain your [.gitignore](http://git-scm.com/docs/gitignore) and ensure it is kept up to date with your composer managed modules without affecting custom ignores. Once installed and setup, it will automatically run every time you install, remove or update modules using composer.
### Installing and enabling the SSAutoGitIgnore package
Include the package in your project by running this command
composer require gdmedia/ss-auto-git-ignore
Edit your composer.json and insert
"scripts": {
"post-update-cmd": "GDM\\SSAutoGitIgnore\\UpdateScript::Go"
}
This will instruct composer to run SSAutoGitIgnore after every update. SSAutoGitIgnore will then ensure composer managed models and themes are correctly added to your [.gitignore](http://git-scm.com/docs/gitignore).
For more information about SSAutoGitIgnore, see the [SSAutoGitIgnore home page](https://github.com/guru-digital/SSAutoGitIgnore/).
For more information about post-updated-cmd and scripts, read the ["Scripts" chapter of the Composer documentation](https://getcomposer.org/doc/articles/scripts.md).
Full example of composer.json with the SSAutoGitIgnore installed and enabled
{
"name": "silverstripe/installer",
"description": "The SilverStripe Framework Installer",
"require": {
"php": ">=5.3.2",
"silverstripe/cms": "3.0.*",
"silverstripe/framework": "3.0.*",
"silverstripe-themes/simple": "*",
"gdmedia/ss-auto-git-ignore": "*"
},
"require-dev": {
"silverstripe/compass": "*",
"silverstripe/docsviewer": "*"
},
"scripts": {
"post-update-cmd": "GDM\\SSAutoGitIgnore\\UpdateScript::Go"
},
"minimum-stability": "dev"
}
# Dev Environments for Contributing Code {#contributing}
So you want to contribute to SilverStripe? Fantastic! You can do this with composer too.
@ -139,7 +203,7 @@ and remove the `@stable` markers from the `silverstripe/cms` and `silverstripe/f
Another `composer update --dev` call will now fetch from the development branch instead.
Note that you can also convert an existing composer project with these steps.
Please read the ["Contributing Code"](/misc/contributing/code) documentation to find out how to
Please read the ["Contributing Code"](/contributing/code) documentation to find out how to
create forks and send pull requests.
# Advanced usage
@ -290,9 +354,9 @@ which triggers their installation into the correct path.
### How do I convert an existing project to Composer?
The easiest way is to follow the [upgrading](/installation/upgrading) instructions
and switch to a newer release. Alternatively, copy the `composer.json` file from
a newer release, and adjust the version settings in the "require" section to your needs.
Copy the `composer.json` file from a newer release, and adjust the
version settings in the "require" section to your needs. Then refer to
the [upgrading documentation](/upgrading).
You'll also need to update your webserver configuration
from there (`.htaccess` or `web.config` files), in order to prevent
web access to the composer-generated files.

View File

@ -93,9 +93,9 @@ This is my `_ss_environment.php` file. I have it placed in `/var`, as each of th
define('SS_DEFAULT_ADMIN_USERNAME', '<email>');
define('SS_DEFAULT_ADMIN_PASSWORD', '<password>');
// This causes errors to be written to the silverstripe.log file in the same directory as this file, so /var.
// Before PHP 5.3.0, you'll need to use dirname(__FILE__) instead of __DIR__
define('SS_ERROR_LOG', __DIR__ . '/silverstripe.log');
// This causes errors to be written to the BASE_PATH/silverstripe.log file.
// Path must be relative to BASE_PATH
define('SS_ERROR_LOG', 'silverstripe.log');
// This is used by sake to know which directory points to which URL
global $_FILE_TO_URL_MAPPING;
@ -119,7 +119,7 @@ This is my `_ss_environment.php` file. I have it placed in `/var`, as each of th
| `SS_ENVIRONMENT_TYPE`| The environment type: dev, test or live.|
| `SS_DEFAULT_ADMIN_USERNAME`| The username of the default admin. This is a user with administrative privileges.|
| `SS_DEFAULT_ADMIN_PASSWORD`| The password of the default admin. This will not be stored in the database.|
| `SS_USE_BASIC_AUTH`| Protect the site with basic auth (good for test sites).<br/>When using CGI/FastCGI with Apache, you will have to add the `RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]` rewrite rule to your `.htaccess` file|
| `SS_USE_BASIC_AUTH`| Protect the site with basic auth (good for test sites).<br/>When using CGI/FastCGI with Apache, you will have to add the `RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]` rewrite rule to your `.htaccess` file|
| `SS_SEND_ALL_EMAILS_TO`| If you set this define, all emails will be redirected to this address.|
| `SS_SEND_ALL_EMAILS_FROM`| If you set this define, all emails will be send from this address.|
| `SS_ERROR_LOG` | |
| `SS_ERROR_LOG` | Relative path to the log file |

View File

@ -117,7 +117,7 @@ Example: `mysite/code/MyClass.php`
To help with namespacing common class names (like Database) it is recommended to use a prefix convention `SS_ClassName` but the filename will remain `ClassName.php`.
See [directory-structure](/topics/directory-structure) for more information.
See [directory structure](directory_structure) for more information.
## Coding Style
@ -149,7 +149,7 @@ When a string is literal (contains no variable substitutions), the apostrophe or
When a literal string itself contains apostrophes, it is permitted to demarcate the string with quotation marks or "double quotes".
:::php
$greeting = "She said 'hello'";
$greeting = "They said 'hello'";
This syntax is preferred over escaping apostrophes as it is much easier to read.
@ -458,5 +458,4 @@ which are licensed under BSD (see [license](http://framework.zend.com/license)).
## Related
* [Topics: CSS](/topics/css)
* [Reference: CMS Architecture](/reference/cms-archirecture)
* [Reference: CMS Architecture](/developer_guides/customising_the_admin_interface/cms_architecture)

View File

@ -178,7 +178,7 @@ would create a new tab called "New Tab", and a single "Author" textfield inside.
</div>
We have added two fields: A simple `[api:TextField]` and a `[api:DateField]`.
There are many more fields available in the default installation, listed in ["form field types"](/developer_guides/forms/fields/common_subclasses).
There are many more fields available in the default installation, listed in ["form field types"](/developer_guides/forms/field_types/common_subclasses).
:::php
return $fields;

View File

@ -9,7 +9,7 @@ This tutorial is deprecated, and has been replaced by Lessons 7, 8, 9, and 10 in
## Overview
This tutorial explores the relationship and management of [DataObjects](/developer_guides/model/dataobject). It builds on the [second tutorial](/tutorials/extending_a_basic_site) where we learnt how to define
This tutorial explores the relationship and management of [DataObjects](api:DataObject). It builds on the [second tutorial](/tutorials/extending_a_basic_site) where we learnt how to define
additional fields on our models, and attach images to them.
## What are we working towards?
@ -65,8 +65,9 @@ Let's create the `Student` and `Project` objects.
The relationships are defined through the `$has_one`
and `$has_many` properties on the objects.
The array keys declares the name of the relationship,
the array values contain the class name (see the ["database structure"](/developer_guides/model/database_structure)
and ["datamodel"](/developer_guides/model/data_model_and_orm) topics for more information).
the array values contain the class name
(see the ["datamodel"](/developer_guides/model/data_model_and_orm)
topic for more information).
As you can see, only the `Project` model extends `Page`,
while `Student` is a plain `DataObject` subclass.

View File

@ -5,7 +5,7 @@ introduction: The tutorials below take a step by step look at how to build a Sil
<div class="alert" markdown="1">
These tutorials are deprecated, and have been replaced by the new [Lessons](http://silverstripe.org/learn/lessons) section.
</div>
[CHIDLREN]
## Video lessons
These include video screencasts, written tutorials and code examples to get you started working with SilverStripe websites.

View File

@ -13,7 +13,7 @@ information.
All data tables in SilverStripe are defined as subclasses of [api:DataObject]. The [api:DataObject] class represents a
single row in a database table, following the ["Active Record"](http://en.wikipedia.org/wiki/Active_record_pattern)
design pattern. Database Columns are is defined as [Data Types](data_types_and_casting) in the static `$db` variable
along with any [relationships](../relations) defined as `$has_one`, `$has_many`, `$many_many` properties on the class.
along with any [relationships](relations) defined as `$has_one`, `$has_many`, `$many_many` properties on the class.
Let's look at a simple example:
@ -222,7 +222,7 @@ Notice that we can step into the loop safely without having to check if `$player
// do something here
}
See the [Lists](../lists) documentation for more information on dealing with [api:SS_List] instances.
See the [Lists](lists) documentation for more information on dealing with [api:SS_List] instances.
## Returning a single DataObject
@ -512,7 +512,7 @@ whenever a new object is created.
<div class="notice" markdown='1'>
Note: Alternatively you can set defaults directly in the database-schema (rather than the object-model). See
[Data Types and Casting](data-types) for details.
[Data Types and Casting](data_types_and_casting) for details.
</div>
## Subclasses

View File

@ -54,7 +54,7 @@ Example: Disallow creation of new players if the currently logged-in player is n
Triggered before executing *delete()* on an existing object.
Example: Checking for a specific [permission](/reference/permission) to delete this type of object. It checks if a
Example: Checking for a specific [permission](permissions) to delete this type of object. It checks if a
member is logged in who belongs to a group containing the permission "PLAYER_DELETE".
:::php

View File

@ -103,7 +103,7 @@ Creates a map based on the first two columns of the query result.
## Related Documentation
* [Introduction to the Data Model and ORM](../data_model_and_orm)
* [Introduction to the Data Model and ORM](data_model_and_orm)
## API Documentation

View File

@ -2,7 +2,7 @@
The [api:SS_List] class is designed to return a flat list of records.
These lists can get quite long, and hard to present on a single list.
[Pagination](/howto/pagination) is one way to solve this problem,
[Pagination](/templates/how_tos/pagination) is one way to solve this problem,
by splitting up the list into multiple pages.
In this howto, we present an alternative to pagination:
@ -144,4 +144,5 @@ The final step is the render this into the template using the [api:GroupedList->
## Related
* [Howto: "Pagination"](/howto/pagination)
* [Howto: "Pagination"](/templates/how_tos/pagination)

View File

@ -393,7 +393,7 @@ A page will normally contain some content and potentially a form of some kind. F
SilverStripe log-in form. If you are on such a page, the `$Form` variable will contain the HTML content of the form.
Placing it just below `$Content` is a good default.
You can add your own forms by implementing new form instances (see the [Forms tutorial](../tutorials/forms)).
You can add your own forms by implementing new form instances (see the [Forms tutorial](/tutorials/forms)).
## Related

View File

@ -22,7 +22,7 @@ Requiring assets from the template is restricted compared to the PHP API.
## PHP Requirements API
It is common practice to include most Requirements either in the *init()*-method of your [controller](../controller), or
It is common practice to include most Requirements either in the *init()*-method of your [controller](../controllers/), or
as close to rendering as possible (e.g. in `[api:FormField]`.
:::php
@ -176,17 +176,19 @@ careful when messing with the order of requirements.
By default, SilverStripe includes all Javascript files at the bottom of the page body, unless there's another script
already loaded, then, it's inserted before the first `<script>` tag. If this causes problems, it can be configured.
**mysite/_config/app.yml**
:::yml
Requirements:
write_js_to_body: true
force_js_to_bottom: true
:::php
Requirements::set_force_js_to_bottom(true);
`Requirements.force_js_to_bottom`, will force SilverStripe to write the Javascript to the bottom of the page body, even
if there is an earlier script tag.
If the Javascript files are preferred to be placed in the `<head>` tag rather than in the `<body>` tag,
`Requirements.write_js_to_body` should be set to false.
:::php
Requirements::set_force_js_to_bottom(true);
## API Documentation
* [api:Requirements]
* [api:Requirements]

View File

@ -31,4 +31,4 @@ top level menu with a nested second level using the `Menu` loop and a `Children`
## Related
* [Template Syntax](../syntax)
* [Common Variables](../command_variables)
* [Common Variables](../common_variables)

View File

@ -145,7 +145,7 @@ Controller actions can use `renderWith` to override this template selection proc
`htmlaction`. `MyCustomTemplate.ss` would be used rather than `TeamsController`.
For more information about templates, inheritance and how to rendering into views, See the
[Templates and Views](templates) documentation.
[Templates and Views](../templates) documentation.
## Link

View File

@ -135,6 +135,14 @@ start parsing variables and the appropriate controller action AFTER the `//`).
## URL Handlers
<div class="alert" markdown="1">
You **must** use the **$url_handlers** static array described here if your URL
pattern does not use the Controller class's default pattern of
`$Action//$ID/$OtherID`. If you fail to do so, and your pattern has more than
2 parameters, your controller will throw the error "I can't handle sub-URLs of
a *class name* object" with HTTP status 404.
</div>
In the above example the URLs were configured using the [api:Director] rules in the **routes.yml** file. Alternatively
you can specify these in your Controller class via the **$url_handlers** static array. This array is processed by the
[api:RequestHandler] at runtime once the `Controller` has been matched.
@ -154,12 +162,42 @@ This is useful when you want to provide custom actions for the mapping of `teams
);
private static $url_handlers = array(
'staff/$ID/$Name' => 'payroll'
'staff/$ID/$Name' => 'payroll',
'coach/$ID/$Name' => 'payroll'
);
The syntax for the `$url_handlers` array users the same pattern matches as the `YAML` configuration rules.
Now lets consider a more complex example from a real project, where using
**$url_handlers** is mandatory. In this example, the URLs are of the form
`http://example.org/feed/go/`, followed by 5 parameters. The PHP controller
class specifies the URL pattern in `$url_handlers`. Notice that it defines 5
parameters.
:::php
class FeedController extends ContentController {
private static $allowed_actions = array('go');
private static $url_handlers = array(
'go/$UserName/$AuthToken/$Timestamp/$OutputType/$DeleteMode' => 'go'
);
public function go() {
$this->validateUser(
$this->request->param('UserName'),
$this->request->param('AuthToken')
);
/* more processing goes here */
}
The YAML rule, in contrast, is simple. It needs to provide only enough
information for the framework to choose the desired controller.
:::yaml
Director:
rules:
'feed': 'FeedController'
## Links
* [api:Controller] API documentation

View File

@ -0,0 +1,384 @@
# UploadField
## Introduction
The UploadField will let you upload one or multiple files of all types, including images. But that's not all it does - it will also link the uploaded file(s) to an existing relation and let you edit the linked files as well. That makes it flexible enough to sometimes even replace the GridField, like for instance in creating and managing a simple gallery.
## Usage
The field can be used in three ways: To upload a single file into a `has_one` relationship,or allow multiple files into a `has_many` or `many_many` relationship, or to act as a stand
alone uploader into a folder with no underlying relation.
## Validation
Although images are uploaded and stored on the filesystem immediately after selection,the value (or values) of this field will not be written to any related record until the record is saved and successfully validated. However, any invalid records will still persist across form submissions until explicitly removed or replaced by the user.
Care should be taken as invalid files may remain within the filesystem until explicitly removed.
### Single fileupload
The following example adds an UploadField to a page for single fileupload, based on a has_one relation:
```php
class GalleryPage extends Page {
private static $has_one = array(
'SingleImage' => 'Image'
);
function getCMSFields() {
$fields = parent::getCMSFields();
$fields->addFieldToTab(
'Root.Upload',
$uploadField = new UploadField(
$name = 'SingleImage',
$title = 'Upload a single image'
)
);
return $fields;
}
}
```
The UploadField will auto-detect the relation based on it's `name` property, and save it into the GalleyPages' `SingleImageID` field. Setting the `setAllowedMaxFileNumber` to 1 will make sure that only one image can ever be uploaded and linked to the relation.
### Multiple fileupload
Enable multiple fileuploads by using a many_many (or has_many) relation. Again, the `UploadField` will detect the relation based on its $name property value:
```php
class GalleryPage extends Page {
private static $many_many = array(
'GalleryImages' => 'Image'
);
function getCMSFields() {
$fields = parent::getCMSFields();
$fields->addFieldToTab(
'Root.Upload',
$uploadField = new UploadField(
$name = 'GalleryImages',
$title = 'Upload one or more images (max 10 in total)'
)
);
$uploadField->setAllowedMaxFileNumber(10);
return $fields;
}
}
class GalleryPage_Controller extends Page_Controller {
}
```
```php
class GalleryImageExtension extends DataExtension {
private static $belongs_many_many = array('Galleries' => 'GalleryPage);
}
```
```yml
Image:
extensions:
- GalleryImageExtension
```
<div class="notice" markdown='1'>
In order to link both ends of the relationship together it's usually advisable to extend Image with the necessary $has_one, $belongs_to, $has_many or $belongs_many_many. In particular, a DataObject with $has_many Images will not work without this specified explicitly.
</div>
## Configuration
### Overview
The field can either be configured on an instance level with the various getProperty and setProperty functions, or globally by overriding the YAML defaults.
See the [Configuration Reference](uploadfield#configuration-reference) section for possible values.
Example: mysite/_config/uploadfield.yml
```yml
after: framework#uploadfield
---
UploadField:
defaultConfig:
canUpload: false
```
### Set a custom folder
This example will save all uploads in the `/assets/customfolder/` folder. If the folder doesn't exist, it will be created.
```php
$fields->addFieldToTab(
'Root.Upload',
$uploadField = new UploadField(
$name = 'GalleryImages',
$title = 'Please upload one or more images' )
);
$uploadField->setFolderName('customfolder');
```
### Limit the allowed filetypes
`AllowedExtensions` defaults to the `File.allowed_extensions` configuration setting, but can be overwritten for each UploadField:
```php
$uploadField->setAllowedExtensions(array('jpg', 'jpeg', 'png', 'gif'));
```
Entire groups of file extensions can be specified in order to quickly limit types to known file categories.
```php
$uploadField->setAllowedFileCategories('image', 'doc');
```
This will limit files to the following extensions: bmp gif jpg jpeg pcx tif png alpha als cel icon ico ps doc docx txt rtf xls xlsx pages ppt pptx pps csv html htm xhtml xml pdf.
`AllowedExtensions` can also be set globally via the [YAML configuration](/developer_guides/configuration/configuration/#configuration-yaml-syntax-and-rules), for example you may add the following into your mysite/_config/config.yml:
```yaml
File:
allowed_extensions:
- 7zip
- xzip
```
### Limit the maximum file size
`AllowedMaxFileSize` is by default set to the lower value of the 2 php.ini configurations: `upload_max_filesize` and `post_max_size`. The value is set as bytes.
NOTE: this only sets the configuration for your UploadField, this does NOT change your server upload settings, so if your server is set to only allow 1 MB and you set the UploadField to 2 MB, uploads will not work.
```php
$sizeMB = 2; // 2 MB
$size = $sizeMB * 1024 * 1024; // 2 MB in bytes
$this->getValidator()->setAllowedMaxFileSize($size);
```
### Preview dimensions
Set the dimensions of the image preview. By default the max width is set to 80 and the max height is set to 60.
```php
$uploadField->setPreviewMaxWidth(100);
$uploadField->setPreviewMaxHeight(100);
```
### Disable attachment of existing files
This can force the user to upload a new file, rather than link to the already existing file library
```php
$uploadField->setCanAttachExisting(false);
```
### Disable uploading of new files
Alternatively, you can force the user to only specify already existing files in the file library
```php
$uploadField->setCanUpload(false);
```
### Automatic or manual upload
By default, the UploadField will try to automatically upload all selected files. Setting the `autoUpload` property to false, will present you with a list of selected files that you can then upload manually one by one:
```php
$uploadField->setAutoUpload(false);
```
### Change Detection
The CMS interface will automatically notify the form containing
an UploadField instance of changes, such as a new upload,
or the removal of an existing upload (through a `dirty` event).
The UI can then choose an appropriate response (e.g. highlighting the "save" button). If the UploadField doesn't save into a relation, there's technically no saveable change (the upload has already happened), which is why this feature can be disabled on demand.
```php
$uploadField->setConfig('changeDetection', false);
```
### Build a simple gallery
A gallery most times needs more then simple images. You might want to add a description, or maybe some settings to define a transition effect for each slide.
First create a [DataExtension](/developer_guides/extending/extensions) like this:
```php
class GalleryImage extends DataExtension {
private static $db = array(
'Description' => 'Text'
);
private static $belongs_many_many = array(
'GalleryPage' => 'GalleryPage'
);
}
```
Now register the DataExtension for the Image class in your mysite/_config/config.yml:
```yml
Image:
extensions:
- GalleryImage
```
<div class="notice" markdown='1'>
Note: Although you can subclass the Image class instead of using a DataExtension, this is not advisable. For instance: when using a subclass, the 'From files' button will only return files that were uploaded for that subclass, it won't recognize any other images!
</div>
### Edit uploaded images
By default the UploadField will let you edit the following fields: *Title, Filename, Owner and Folder*. The fileEditFields` configuration setting allows you you alter these settings. One way to go about this is create a `getCustomFields` function in your GalleryImage object like this:
```php
class GalleryImage extends DataExtension {
...
function getCustomFields() {
$fields = new FieldList();
$fields->push(new TextField('Title', 'Title'));
$fields->push(new TextareaField('Description', 'Description'));
return $fields;
}
}
```
Then, in your GalleryPage, tell the UploadField to use this function:
```php
$uploadField->setFileEditFields('getCustomFields');
```
In a similar fashion you can use 'setFileEditActions' to set the actions for the editform, or 'fileEditValidator' to determine the validator (e.g. RequiredFields).
### Configuration Reference
- `setAllowedMaxFileNumber`: (int) php validation of allowedMaxFileNumber only works when a db relation is available, set to null to allow unlimited if record has a has_one and allowedMaxFileNumber is null, it will be set to 1.
- `setAllowedFileExtensions`: (array) List of file extensions allowed.
- `setAllowedFileCategories`: (array|string) List of types of files allowed. May be any of 'image', 'audio', 'mov', 'zip', 'flash', or 'doc'.
- `setAutoUpload`: (boolean) Should the field automatically trigger an upload once a file is selected?
- `setCanAttachExisting`: (boolean|string) Can the user attach existing files from the library. String values are interpreted as permission codes.
- `setCanPreviewFolder`: (boolean|string) Can the user preview the folder files will be saved into? String values are interpreted as permission codes.
- `setCanUpload`: (boolean|string) Can the user upload new files, or just select from existing files. String values are interpreted as permission codes.
- `setDownloadTemplateName`: (string) javascript template used to display already uploaded files, see javascript/UploadField_downloadtemplate.js.
- `setFileEditFields`: (FieldList|string) FieldList $fields or string $name (of a method on File to provide a fields) for the EditForm (Example: 'getCMSFields').
- `setFileEditActions`: (FieldList|string) FieldList $actions or string $name (of a method on File to provide a actions) for the EditForm (Example: 'getCMSActions').
- `setFileEditValidator`: (string) Validator (eg RequiredFields) or string $name (of a method on File to provide a Validator) for the EditForm (Example: 'getCMSValidator').
- `setOverwriteWarning`: (boolean) Show a warning when overwriting a file.
- `setPreviewMaxWidth`: (int).
- `setPreviewMaxHeight`: (int).
- `setTemplateFileButtons`: (string) Template name to use for the file buttons.
- `setTemplateFileEdit`: (string) Template name to use for the file edit form.
- `setUploadTemplateName`: (string) javascript template used to display uploading files, see javascript/UploadField_uploadtemplate.js.
- `setCanPreviewFolder`: (boolean|string) Is the upload folder visible to uploading users? String values are interpreted as permission codes.
Certain default values for the above can be configured using the YAML config system.
```yaml
UploadField:
defaultConfig:
autoUpload: true
allowedMaxFileNumber:
canUpload: true
canAttachExisting: 'CMS_ACCESS_AssetAdmin'
canPreviewFolder: true
previewMaxWidth: 80
previewMaxHeight: 60
uploadTemplateName: 'ss-uploadfield-uploadtemplate'
downloadTemplateName: 'ss-uploadfield-downloadtemplate'
overwriteWarning: true # Warning before overwriting existing file (only relevant when Upload: replaceFile is true)
```
The above settings can also be set on a per-instance basis by using `setConfig` with the appropriate key.
You can also configure the underlying `[api:Upload]` class, by using the YAML config system.
```yaml
Upload:
# Globally disables automatic renaming of files and displays a warning before overwriting an existing file
replaceFile: true
uploads_folder: 'Uploads'
```
## Using the UploadField in a frontend form
The UploadField can be used in a frontend form, given that sufficient attention is given to the permissions granted to non-authorised users.
By default Image::canDelete and Image::canEdit do not require admin privileges, so make sure you override the methods in your Image extension class.
For instance, to generate an upload form suitable for saving images into a user-defined gallery the below code could be used:
*In GalleryPage.php:*
```php
<?php
class GalleryPage extends Page {}
class GalleryPage_Controller extends Page_Controller {
private static $allowed_actions = array('Form');
public function Form() {
$fields = new FieldList(
new TextField('Title', 'Title', null, 255),
$field = new UploadField('Images', 'Upload Images')
);
$field->setCanAttachExisting(false); // Block access to SilverStripe assets library
$field->setCanPreviewFolder(false); // Don't show target filesystem folder on upload field
$field->relationAutoSetting = false; // Prevents the form thinking the GalleryPage is the underlying object
$actions = new FieldList(new FormAction('submit', 'Save Images'));
return new Form($this, 'Form', $fields, $actions, null);
}
public function submit($data, Form $form) {
$gallery = new Gallery();
$form->saveInto($gallery);
$gallery->write();
return $this;
}
}
```
*Gallery.php:*
```php
<?php
class Gallery extends DataObject {
private static $db = array(
'Title' => 'Varchar(255)'
);
private static $many_many = array(
'Images' => 'Image'
);
}
```
*ImageExtension.php:*
```php
<?php
class ImageExtension extends DataExtension {
private static $belongs_many_many = array(
'Gallery' => 'Gallery'
);
function canEdit($member) {
// WARNING! This affects permissions on ALL images. Setting this incorrectly can restrict
// access to authorised users or unintentionally give access to unauthorised users if set incorrectly.
return Permission::check('CMS_ACCESS_AssetAdmin');
}
}
```
*mysite/_config/config.yml*
```yml
Image:
extensions:
- ImageExtension
```

View File

@ -1,6 +1,6 @@
# How to add a custom action to a GridField row
In a [GridField](/reference/grid-field) instance each table row can have a
In a [GridField](../field_types/gridfield) instance each table row can have a
number of actions located the end of the row such as edit or delete actions.
Each action is represented as a instance of a specific class
(e.g [api:GridFieldEditButton]) which has been added to the `GridFieldConfig`
@ -79,7 +79,7 @@ below:
While we're working on the code, to add this new action to the `GridField`, add
a new instance of the class to the [api:GridFieldConfig] object. The `GridField`
[Reference](/reference/grid-field) documentation has more information about
[Reference](../field_types/gridfield) documentation has more information about
manipulating the `GridFieldConfig` instance if required.
:::php
@ -142,6 +142,6 @@ message to the user interface indicating a successful message.
## Related
* [GridField Reference](/reference/grid-field)
* [ModelAdmin: A UI driven by GridField](/reference/modeladmin)
* [Tutorial 5: Dataobject Relationship Management](/tutorials/5-dataobject-relationship-management)
* [GridField Reference](/developer_guides/forms/field_types/gridfield)
* [ModelAdmin: A UI driven by GridField](/developer_guides/customising_the_admin_interface/modeladmin)
* [Tutorial 5: Dataobject Relationship Management](/tutorials/dataobject_relationship_management)

View File

@ -5,7 +5,7 @@ title: How to Publish a SilverStripe module
If you wish to submit your module to our public directory, you take responsibility for a certain level of code quality,
adherence to conventions, writing documentation, and releasing updates.
SilverStripe uses [Composer](../../getting_started/composer/) to manage module releases and dependencies between
SilverStripe uses [Composer](../../../getting_started/composer/) to manage module releases and dependencies between
modules. If you plan on releasing your module to the public, ensure that you provide a `composer.json` file in the root
of your module containing the meta-data about your module.

View File

@ -309,7 +309,7 @@ equal the class names they manage.
## Related Documentation
* [How to use a FixtureFactory](how_to/fixturefactories/)
* [How to use a FixtureFactory](how_tos/fixturefactories/)
## API Documentation

View File

@ -0,0 +1,29 @@
title: Testing Glossary
<dl>
<dt>Assertion<dd>A predicate statement that must be true when a test runs.
<dt>Behat<dd>A behaviour-driven testing library used with SilverStripe as a higher-level alternative to the `FunctionalTest` API, see <http://behat.org>.
<dt>Test Case<dd>The atomic class type in most unit test frameworks. New unit tests are created by inheriting from the base test case.
<dt>Test Suite<dd>Also known as a 'test group', a composite of test cases, used to collect individual unit tests into packages, allowing all tests to be run at once.
<dt>Fixture<dd>Usually refers to the runtime context of a unit test - the environment and data prerequisites that must be in place in order to run the test and expect a particular outcome. Most unit test frameworks provide methods that can be used to create fixtures for the duration of a test - `setUp` - and clean them up after the test is done - `tearDown`.
<dt>Refactoring<dd>A behavior preserving transformation of code. If you change the code, while keeping the actual functionality the same, it is refactoring. If you change the behavior or add new functionality it's not.
<dt>Smell<dd>A code smell is a symptom of a problem. Usually refers to code that is structured in a way that will lead to problems with maintenance or understanding.
<dt>Spike<dd>A limited and throwaway sketch of code or experiment to get a feel for how long it will take to implement a certain feature, or a possible direction for how that feature might work.
<dt>Test Double<dd>Also known as a 'Substitute'. A general term for a dummy object that replaces a real object with the same interface. Substituting objects is useful when a real object is difficult or impossible to incorporate into a unit test.
**Fake Object**: A substitute object that simply replaces a real object with the same interface, and returns a pre-determined (usually fixed) value from each method.
<dt>Mock Object<dd>A substitute object that mimics the same behavior as a real object (some people think of mocks as "crash test dummy" objects). Mocks differ from other kinds of substitute objects in that they must understand the context of each call to them, setting expectations of which, and what order, methods will be invoked and what parameters will be passed.
<dt>Test-Driven Development (TDD)<dd>A style of programming where tests for a new feature are constructed before any code is written. Code to implement the feature is then written with the aim of making the tests pass. Testing is used to understand the problem space and discover suitable APIs for performing specific actions.
<dt>Behavior Driven Development (BDD)<dd>An extension of the test-driven programming style, where tests are used primarily for describing the specification of how code should perform. In practice, there's little or no technical difference - it all comes down to language. In BDD, the usual terminology is changed to reflect this change of focus, so _Specification_ is used in place of _Test Case_, and _should_ is used in place of _expect_ and _assert_.
</dl>

View File

@ -12,18 +12,15 @@ SilverStripe uses [PHPUnit](http://www.phpunit.de) for unit tests, and the frame
process of creating and managing tests.
If you're more familiar with unit testing, but want a refresher of some of the concepts and terminology, you can browse
the [Testing Glossary](glossary). To get started now, follow the installation instructions below, and check
[Troubleshooting](testing-guide-troubleshooting) in case you run into any problems.
the [Testing Glossary](testing_glossary). To get started now, follow the installation instructions below.
If you are familiar with PHP coding but new to unit testing then check out Mark's presentation [Getting to Grips with SilverStripe Testing](http://www.slideshare.net/maetl/getting-to-grips-with-silverstripe-testing).
[Why Unit Test?](why-should-i-test) will give you reasons why you should be testing your code.
You should also read over [the PHPUnit manual](http://www.phpunit.de/manual/current/en/). It provides a lot of
fundamental concepts that we build on in this documentation.
Unit tests are not included in the normal SilverStripe downloads so you need to install them through git repositories
([installation instructions](/installation/composer)).
([installation instructions](/getting_started/composer)).
## Install with Composer
@ -108,7 +105,7 @@ All command-line arguments are documented on
### Via the "sake" Wrapper on Command Line
The [sake](/topics/commandline) executable that comes with SilverStripe can trigger a customized
The [sake](/developer_guides/cli/) executable that comes with SilverStripe can trigger a customized
`[api:TestRunner]` class that handles the PHPUnit configuration and output formatting.
While the custom test runner a handy tool, its also more limited than using `phpunit` directly,
particularly around formatting test output.

View File

@ -24,7 +24,7 @@ Append the option and corresponding value to your URL in your browser's address
| URL Variable | | Values | | Description |
| ------------ | | ------ | | ----------- |
| isDev | | 1 | | Put the site into [development mode](/topics/debugging), enabling debugging messages to the browser on a live server. For security, you'll be asked to log in with an administrator log-in. Will persist for the current browser session. |
| isDev | | 1 | | Put the site into [development mode](../), enabling debugging messages to the browser on a live server. For security, you'll be asked to log in with an administrator log-in. Will persist for the current browser session. |
| isTest | | 1 | | See above. |
| debug | | 1 | | Show a collection of debugging information about the director / controller operation |
| debug_request | | 1 | | Show all steps of the request from initial `[api:HTTPRequest]` to `[api:Controller]` to Template Rendering |

View File

@ -3,7 +3,7 @@ summary: Learn how to identify errors in your application and best practice for
# Debugging
SilverStripe can be a large and complex framework to debug, but there are ways to make debugging less painful. In this
guide we show the basics on defining the correct [Environment Type](environment_type) for your application and other
guide we show the basics on defining the correct [Environment Type](environment_types) for your application and other
built-in helpers for dealing with application errors.
[CHILDREN]

View File

@ -22,15 +22,15 @@ will invalidate the cache after a given amount of time has expired (default 10 m
Here are some more complex examples:
:::ss
<% cached 'database', LastEdited %>
<% cached 'database', $LastEdited %>
<!-- that updates every time the record changes. -->
<% end_cached %>
<% cached 'loginblock', CurrentMember.ID %>
<% cached 'loginblock', $CurrentMember.ID %>
<!-- cached unique to the user. i.e for user 2, they will see a different cache to user 1 -->
<% end_cached %>
<% cached 'loginblock', LastEdited, CurrentMember.isAdmin %>
<% cached 'loginblock', $LastEdited, $CurrentMember.isAdmin %>
<!-- recached when block object changes, and if the user is admin -->
<% end_cached %>
@ -53,13 +53,13 @@ user does not influence your template content, you can update this key as below;
Often you want to invalidate a cache when any object in a set of objects change, or when the objects in a relationship
change. To do this, SilverStripe introduces the concept of Aggregates. These calculate and return SQL aggregates
on sets of [api:DataObject]s - the most useful for us being the `Max` aggregate.
on sets of [api:DataObject]s - the most useful for us being the `max` aggregate.
For example, if we have a menu, we want that menu to update whenever _any_ page is edited, but would like to cache it
otherwise. By using aggregates, we do that like this:
:::ss
<% cached 'navigation', List(SiteTree).max(LastEdited), List(SiteTree).count() %>
<% cached 'navigation', $List('SiteTree').max('LastEdited'), $List('SiteTree').count() %>
The cache for this will update whenever a page is added, removed or edited.
@ -67,10 +67,10 @@ If we have a block that shows a list of categories, we can make sure the cache u
or edited
:::ss
<% cached 'categorylist', List(Category).max(LastEdited), List(Category).count() %>
<% cached 'categorylist', $List('Category').max('LastEdited'), $List('Category').count() %>
<div class="notice" markdown="1">
Note the use of both `.max(LastEdited)` and `.count()` - this takes care of both the case where an object has been
Note the use of both `.max('LastEdited')` and `.count()` - this takes care of both the case where an object has been
edited since the cache was last built, and also when an object has been deleted since the cache was last built.
</div>
@ -78,7 +78,7 @@ We can also calculate aggregates on relationships. A block that shows the curren
whenever the relationship `Member::$has_many = array('Favourites' => Favourite')` changes.
:::ss
<% cached 'favourites', CurrentMember.ID, CurrentMember.Favourites.max(LastEdited) %>
<% cached 'favourites', $CurrentMember.ID, $CurrentMember.Favourites.max('LastEdited') %>
## Cache key calculated in controller
@ -100,7 +100,7 @@ extract that logic into the controller.
Then using that function in the cache key:
:::ss
<% cached FavouriteCacheKey %>
<% cached $FavouriteCacheKey %>
## Cache blocks and template changes
@ -118,7 +118,7 @@ data updates.
For instance, if we show some blog statistics, but are happy having them be slightly stale, we could do
:::ss
<% cached 'blogstatistics', Blog.ID %>
<% cached 'blogstatistics', $Blog.ID %>
which will invalidate after the cache lifetime expires. If you need more control than that (cache lifetime is
@ -133,7 +133,7 @@ configurable only on a site-wide basis), you could add a special function to you
and then use it in the cache key
:::ss
<% cached 'blogstatistics', Blog.ID, BlogStatisticsCounter %>
<% cached 'blogstatistics', $Blog.ID, $BlogStatisticsCounter %>
## Cache block conditionals
@ -146,7 +146,7 @@ Following on from the previous example, you might wish to only cache slightly-st
heavy load:
:::ss
<% cached 'blogstatistics', Blog.ID if HighLoad %>
<% cached 'blogstatistics', $Blog.ID if $HighLoad %>
By adding a `HighLoad` function to your `Page_Controller`, you could enable or disable caching dynamically.
@ -155,7 +155,7 @@ To cache the contents of a page for all anonymous users, but dynamically calcula
use something like:
:::ss
<% cached unless CurrentUser %>
<% cached unless $CurrentUser %>
## Uncached
@ -178,10 +178,10 @@ portion dynamic, without having to include any member info in the page's cache k
An example:
:::ss
<% cached LastEdited %>
<% cached $LastEdited %>
Our wonderful site
<% cached Member.ID %>
<% cached $Member.ID %>
Welcome $Member.Name
<% end_cached %>
@ -196,7 +196,7 @@ Cache conditionals and the uncached tag also work in the same nested manner. Sin
could also write the last example as:
:::ss
<% cached LastEdited %>
<% cached $LastEdited %>
Our wonderful site
<% uncached %>
@ -214,7 +214,7 @@ letting you know if you've done this. You can often get around this using aggreg
Failing example:
:::ss
<% cached LastEdited %>
<% cached $LastEdited %>
<% loop $Children %>
<% cached LastEdited %>
@ -227,9 +227,9 @@ Failing example:
Can be re-written as:
:::ss
<% cached LastEdited %>
<% cached $LastEdited %>
<% cached AllChildren.max(LastEdited) %>
<% cached $AllChildren.max('LastEdited') %>
<% loop $Children %>
$Name
<% end_loop %>

View File

@ -17,7 +17,7 @@ Flushing the various manifests is performed through a GET
parameter (`flush=1`). Since this action requires more server resources than normal requests,
executing the action is limited to the following cases when performed via a web request:
* The [environment](/topics/environment-management) is in "dev mode"
* The [environment](../getting_started/environment_management) is in "dev mode"
* A user is logged in with ADMIN permissions
* An error occurs during startup

View File

@ -7,7 +7,7 @@ See our "[Release Process](/misc/release-process#security-releases) on how to re
## SQL Injection
The [coding-conventions](/misc/coding-conventions) help guard against SQL injection attacks but still require developer
The [coding-conventions](/getting_started/coding_conventions) help guard against SQL injection attacks but still require developer
diligence: ensure that any variable you insert into a filter / sort / join clause is either parameterised, or has been
escaped.
@ -95,8 +95,8 @@ result in *double escaping* and alters the actually saved data (e.g. by adding s
### Manual escaping
As a rule of thumb, whenever you're creating SQL queries (or just chunks of SQL) you should use parameterisation,
but there may be cases where you need to take care of escaping yourself. See [coding-conventions](/misc/coding-conventions)
and [datamodel](/topics/datamodel) for ways to parameterise, cast, and convert your data.
but there may be cases where you need to take care of escaping yourself. See [coding-conventions](/getting_started/coding-conventions)
and [datamodel](/developer_guides/model) for ways to parameterise, cast, and convert your data.
* `SQLQuery`
* `DB::query()`

View File

@ -216,7 +216,7 @@ $service->request('service.json', 'GET', null, null, $curlOptions);
## How to's
* [Embed an RSS Feed](how_to/embed_rss)
* [Embed an RSS Feed](how_tos/embed_rss)
## API Documentation

View File

@ -11,7 +11,7 @@ The default output of a [api:SearchContext] is either a [api:SQLQuery] object fo
[api:DataObject] instance.
<div class="notice" markdown="1">
[api:SearchContext] is mainly used by [ModelAdmin](../customising_the_cms/modeladmin).
[api:SearchContext] is mainly used by [ModelAdmin](../customising_the_admin_interface/modeladmin).
</div>
## Usage

View File

@ -80,7 +80,7 @@ not PHP's built-in [date()](http://nz.php.net/manual/en/function.date.php).
### Language Names
SilverStripe comes with a built-in list of common languages, listed by locale and region.
They can be accessed via the `i18n.common_languages` and `i18n.common_locales` [config setting](/topics/configuration).
They can be accessed via the `i18n.common_languages` and `i18n.common_locales` [config setting](/developer_guides/configuration).
In order to add a value, add the following to your `config.yml`:
@ -125,7 +125,7 @@ Date- and time related form fields support i18n ([api:DateField], [api:TimeField
$field->setConfig('dateformat', 'dd. MMMM YYYY'); // sets typical 'de_DE' date format, shows as "23. Juni 1982"
Defaults can be applied globally for all field instances through the `DateField.default_config`
and `TimeField.default_config` [configuration arrays](/topics/configuration).
and `TimeField.default_config` [configuration arrays](/developer_guides/configuration).
If no 'locale' default is set on the field, [api:i18n::get_locale()] will be used.
**Important:** Form fields in the CMS are automatically configured according to the profile settings for the logged-in user (`Member->Locale`, `Member->DateFormat` and `Member->TimeFormat`). This means that in most cases,
@ -408,7 +408,7 @@ The `ss.i18n` object contain a couple functions to help and replace dynamic vari
## Links
* [Help to translate](/misc/contribute/translation) - Instructions for online collaboration to translate core
* [Help to translate](/misc/translation-process) - Instructions for adding translation to your own modules
* [Help to translate](../../contributing/translations) - Instructions for online collaboration to translate core
* [Help to translate](../../contributing/translation_process) - Instructions for adding translation to your own modules
* [http://www.i18nguy.com/](http://www.i18nguy.com/)
* [balbus.tk i18n notes](http://www.balbus.tk/internationalize)

View File

@ -13,7 +13,7 @@ All files, images and folders in the 'assets' directory are stored in the databa
| `Title` | The optional, human-readable title of the file for display only (doesn't apply to folders). |
| `Filename` | The path to the file/folder, relative to the webroot. For example 'assets/images/my-image.jpg', or 'assets/images/' for a folder. |
| `Content` | Typically unused, but handy for a textual representation of files. For example for fulltext indexing of PDF documents. |
| `ShowInSearch` | Whether the file should be shown in search results, defaults to '1'. See ["Tutorial 4 - Site Search"](/tutorials/4-site-search) for enabling search. |
| `ShowInSearch` | Whether the file should be shown in search results, defaults to '1'. See ["Tutorial 4 - Site Search"](/tutorials/site_search) for enabling search. |
| `ParentID` | The ID of the parent Folder that this File/Folder is in. A ParentID of '0' indicates that the File/Folder is in the 'assets' directory. |
| `OwnerID` | The ID of the Member that 'owns' the File/Folder (not related to filesystem permissions). |
@ -37,4 +37,4 @@ You may also notice the 'Sync files' button (highlighted below). This button all
## Upload
Files can be managed through a `FileField` or an `UploadField`. The `[api:FileField]` class provides a simple HTML input with a type of "file", whereas an `[api:UploadField]` provides a much more feature-rich field (including AJAX-based uploads, previews, relationship management and file data management). See [`Reference - UploadField`](/reference/uploadfield) for more information about how to use the `UploadField` class.
Files can be managed through a `FileField` or an `UploadField`. The `[api:FileField]` class provides a simple HTML input with a type of "file", whereas an `[api:UploadField]` provides a much more feature-rich field (including AJAX-based uploads, previews, relationship management and file data management). See [`Reference - UploadField`](/developer_guides/forms/field_types/uploadfield) for more information about how to use the `UploadField` class.

View File

@ -10,7 +10,7 @@ It uses the framework's knowledge about the model to provide sensible defaults,
of lines of code, while still providing a solid base for customization.
<div class="info" markdown="1">
The interface is mainly powered by the [api:GridField] class ([documentation](../forms/fields/gridfield)), which can
The interface is mainly powered by the [api:GridField] class ([documentation](../forms/field_types/gridfield)), which can
also be used in other areas of your application.
</div>
@ -146,7 +146,7 @@ class (see [SearchContext](../search/searchcontext) docs for details).
## Displaying Results
The results are shown in a tabular listing, powered by the [GridField](../forms/fields/gridfield), more specifically
The results are shown in a tabular listing, powered by the [GridField](../forms/field_types/gridfield), more specifically
the [api:GridFieldDataColumns] component. This component looks for a [api:DataObject::$summary_fields] static on your
model class, where you can add or remove columns. To change the title, use [api:DataObject::$field_labels].
@ -320,9 +320,9 @@ To customize the exported columns, create a new method called `getExportFields`
## Related Documentation
* [GridField](../forms/fields/gridfield)
* [GridField](../forms/field_types/gridfield)
* [Permissions](../security/permissions)
* [SeachContext](../search/seachcontext)
* [SearchContext](../search/searchcontext)
## API Documentation

View File

@ -66,7 +66,7 @@ Layout manager will automatically apply algorithms to the children of `.cms-cont
For detailed discussion on available algorithms refer to
[jLayout algorithms](https://github.com/bramstein/jlayout#layout-algorithms).
Our [Howto: Extend the CMS Interface](../howto/extend-cms-interface) has a practical example on how to add a bottom
Our [Howto: Extend the CMS Interface](how_tos/extend_cms_interface) has a practical example on how to add a bottom
panel to the CMS UI.
### Methods
@ -115,6 +115,6 @@ The parameters are as follows:
## Related
* [Reference: CMS Architecture](../reference/cms-architecture)
* [Reference: Preview](../reference/preview)
* [Howto: Extend the CMS Interface](../howto/extend-cms-interface)
* [Reference: CMS Architecture](cms_architecture)
* [Reference: Preview](preview)
* [Howto: Extend the CMS Interface](how_tos/extend_cms_interface)

View File

@ -69,7 +69,7 @@ Note how the configuration happens in different entwine namespaces
}(jQuery));
Load the file in the CMS via setting adding 'mysite/javascript/MyLeftAndMain.Preview.js'
to the `LeftAndMain.extra_requirements_javascript` [configuration value](/topics/configuration)
to the `LeftAndMain.extra_requirements_javascript` [configuration value](../configuration)
:::yml
LeftAndMain:
@ -79,7 +79,7 @@ to the `LeftAndMain.extra_requirements_javascript` [configuration value](/topics
In order to find out which configuration values are available, the source code
is your best reference at the moment - have a look in `framework/admin/javascript/LeftAndMain.Preview.js`.
To understand how layouts are handled in the CMS UI, have a look at the
[CMS Architecture](/reference/cms-architecture) guide.
[CMS Architecture](cms_architecture) guide.
## Enabling preview
@ -146,7 +146,7 @@ You can find out current size by calling:
## Preview modes
Preview modes map to the modes supported by the _threeColumnCompressor_ layout
algorithm, see [layout reference](../reference/layout) for more details. You
algorithm, see [layout reference](cms_layout) for more details. You
can change modes by calling:
```js
@ -184,4 +184,4 @@ previewable content is loaded.
## Related
* [Reference: Layout](../reference/layout)
* [Reference: Layout](cms_layout)

View File

@ -13,7 +13,7 @@ feel familiar to you. This is just a quick run down to get you started
with some special conventions.
For a more practical-oriented approach to CMS customizations, refer to the
[Howto: Extend the CMS Interface](../howto/extend-cms-interface) which builds
[Howto: Extend the CMS Interface](../how_tos/extend_cms_interface) which builds
## Markup and Style Conventions
@ -47,7 +47,7 @@ As there's a whole lot of CSS driving the CMS, we have certain best practives ar
* Use jQuery UI's built-in styles where possible, e.g. `ui-widget` for a generic container, or `ui-state-highlight`
to highlight a specific component. See the [jQuery UI Theming API](http://jqueryui.com/docs/Theming/API) for a full list.
See our [system requirements](../installation/server-requirements) for a list of supported browsers.
See our [system requirements](/getting_started/server_requirements) for a list of supported browsers.
## Templates and Controllers
@ -89,7 +89,7 @@ The various panels and UI components within them are loosely coupled to the layo
attribute. The layout is triggered on the top element and cascades into children, with a `redraw` method defined on
each panel and UI component that needs to update itself as a result of layouting.
Refer to [Layout reference](../reference/layout) for further information.
Refer to [Layout reference](cms_layout) for further information.
## Forms
@ -148,7 +148,7 @@ correctly configured form.
[jQuery.entwine](https://github.com/hafriedlander/jquery.entwine) is a thirdparty library
which allows us to attach behaviour to DOM elements in a flexible and structured mannger.
It replaces the `behaviour.js` library used in previous versions of the CMS interface.
See [Topics: JavaScript](../topics/javascript) for more information on how to use it.
See [JavaScript Development](javascript_development) for more information on how to use it.
In the CMS interface, all entwine rules should be placed in the "ss" entwine namespace.
If you want to call methods defined within these rules outside of entwine logic,
you have to use this namespace, e.g. `$('.cms-menu').entwine('ss').collapse()`.
@ -222,7 +222,7 @@ In order for this to work, the CMS templates declare certain sections as "PJAX f
through a `data-pjax-fragment` attribute. These names correlate to specific
rendering logic in the PHP controllers, through the `[api:PjaxResponseNegotiator]` class.
Through a custom `X-Pjax` HTTP header, the client can declare which view he's expecting,
Through a custom `X-Pjax` HTTP header, the client can declare which view they're expecting,
through identifiers like `CurrentForm` or `Content` (see `[api:LeftAndMain->getResponseNegotiator()]`).
These identifiers are passed to `loadPanel()` via the `pjax` data option.
The HTTP response is a JSON object literal, with template replacements keyed by their Pjax fragment.
@ -433,7 +433,7 @@ The CMS tree for viewing hierarchical structures (mostly pages) is powered
by the [jstree](http://jstree.com) library. It is configured through
`framework/admin/javascript/LeftAndMain.Tree.js`, as well as some
HTML5 metadata generated on its container (see the `data-hints` attribute).
For more information, see the [Howto: Customize the CMS tree](../howto/customize-cms-tree).
For more information, see the [Howto: Customize the CMS tree](../how_tos/customize_cms_tree).
Note that a similar tree logic is also used for the
form fields to select one or more entries from those hierarchies
@ -538,8 +538,8 @@ through the `PjaxResponseNegotiator` class (see above).
## Related
* [Howto: Extend the CMS Interface](../howto/extend-cms-interface)
* [Howto: Customize the CMS tree](../howto/customize-cms-tree)
* [Reference: ModelAdmin](../reference/modeladmin)
* [Reference: Layout](../reference/layout)
* [Topics: Rich Text Editing](../topics/rich-text-editing)
* [Howto: Extend the CMS Interface](how_tos/extend_cms_interface)
* [Howto: Customize the CMS tree](how_tos/customize_cms_tree)
* [ModelAdmin API](api:ModelAdmin)
* [Reference: Layout](cms_layout)
* [Rich Text Editing](/developer_guides/forms/field_types/htmleditorfield)

View File

@ -14,7 +14,7 @@ This how-to will walk you through creation of a "Clean-up" button with two appea
* active: "Clean-up now" green constructive button if the actions can be performed
* netural: "Cleaned" default button if the action does not need to be done
The controller code that goes with this example is listed in [Extend CMS Interface](../reference/extend-cms-interface).
The controller code that goes with this example is listed in [Extend CMS Interface](extend_cms_interface).
## Backend support ##
@ -156,4 +156,4 @@ cases.
## Summary ##
The code presented gives you a fully functioning alternating button, similar to the defaults that come with the the CMS.
These alternating buttons can be used to give user the advantage of visual feedback upon his actions.
These alternating buttons can be used to give user the advantage of visual feedback upon their actions.

View File

@ -27,4 +27,4 @@ more complex fields like `GridField`, `UploadField`
or `DropdownField` with the chosen.js behaviour applied.
Note: For more advanced help text we recommend using
[Custom form field templates](/topics/forms#custom-form-field-templates);
[Custom form field templates](../form_templates);

View File

@ -7,10 +7,10 @@ SilverStripe will automatically create a new `[api:CMSMenuItem]` for it
The most popular extension of LeftAndMain is a `[api:ModelAdmin]` class, so
for a more detailed introduction to creating new `ModelAdmin` interfaces, read
the [ModelAdmin reference](../reference/modeladmin).
the [ModelAdmin reference](../modeladmin).
In this document we'll take the `ProductAdmin` class used in the
[ModelAdmin reference](../reference/modeladmin#setup) and so how we can change
[ModelAdmin reference](../modeladmin#setup) and so how we can change
the menu behaviour by using the `$menu_title` and `$menu_icon` statics to
provide a custom title and icon.
@ -43,7 +43,7 @@ In order to localize the menu title in different languages, use the
the i18n text collection.
For more information on language and translations, please refer to the
[i18n](../reference/ii8n) docs.
[i18n](../../ii8n) docs.
## Adding an external link to the menu
@ -85,7 +85,7 @@ button configuration.
To have the link appear, make sure you add the extension to the `LeftAndMain`
class. For more information about configuring extensions see the
[DataExtension reference](../reference/dataextension).
[extensions reference](../extending/extensions).
:::php
LeftAndMain::add_extension('CustomLeftAndMain')
@ -93,4 +93,4 @@ class. For more information about configuring extensions see the
## Related
* [How to extend the CMS interface](extend-cms-interface)
* [How to extend the CMS interface](extend_cms_interface)

View File

@ -62,7 +62,7 @@ or across page types with common characteristics.
}
}
Now you just need to enable the extension in your [configuration file](/topics/configuration).
Now you just need to enable the extension in your [configuration file](../../configuration).
// mysite/_config/config.yml
LeftAndMain:

View File

@ -55,7 +55,7 @@ with the CMS interface. Paste the following content into a new file called
.bookmarked-link.first {margin-top: 1em;}
Load the new CSS file into the CMS, by setting the `LeftAndMain.extra_requirements_css`
[configuration value](/topics/configuration).
[configuration value](../../configuration).
:::yml
LeftAndMain:
@ -85,7 +85,7 @@ and insert the following code.
}
}
Enable the extension in your [configuration file](/topics/configuration)
Enable the extension in your [configuration file](../../configuration)
:::yml
SiteTree:
@ -114,7 +114,7 @@ Add the following code to a new file `mysite/code/BookmarkedLeftAndMainExtension
}
}
Enable the extension in your [configuration file](/topics/configuration)
Enable the extension in your [configuration file](../../configuration)
:::yml
LeftAndMain:
@ -191,11 +191,11 @@ Empty tabs will be automatically removed from the `FieldList` to prevent clutter
</div>
New actions will need associated controller handlers to work. You can use a
`LeftAndMainExtension` to provide one. Refer to [Controller documentation](../topics/controller)
`LeftAndMainExtension` to provide one. Refer to [Controller documentation](../../controllers)
for instructions on setting up handlers.
To make the actions more user-friendly you can also use alternating buttons as
detailed in the [CMS Alternating Button](../reference/cms-alternating-button)
detailed in the [CMS Alternating Button](cms_alternating_button)
how-to.
## Summary
@ -207,7 +207,7 @@ blocks and concepts for more complex extensions as well.
## Related
* [Reference: CMS Architecture](../reference/cms-architecture)
* [Reference: Layout](../reference/layout)
* [Topics: Rich Text Editing](../topics/rich-text-editing)
* [CMS Alternating Button](../howto/cms-alternating-button)
* [Reference: CMS Architecture](../cms_architecture)
* [Reference: Layout](../cms_layout)
* [Rich Text Editing](/developer_guides/forms/field_types/htmleditorfield)
* [CMS Alternating Button](cms_alternating_button)

View File

@ -64,14 +64,14 @@ a `tests/` folder, unless tests are executed.
The `[api:SS_TemplateManifest]` class builds a manifest of all templates present in a directory,
in both modules and themes. Templates in `tests/` folders are automatically excluded.
The chapter on [template inheritance](../templates/template-inheritance) provides more details
The chapter on [template inheritance](../templates/template_inheritance) provides more details
on its operation.
## Config Manifest
The `[api:SS_ConfigManifest]` loads builds a manifest of configuration items,
for both PHP and YAML. It also takes care of ordering and merging configuration fragments.
The chapter on [configuration](/topics/configuration) has more details.
The chapter on [configuration](../configuration) has more details.
## Flushing

View File

@ -103,14 +103,14 @@ before handing control off to SilverStripe's own `main.php`.
## Routing and Request Handling
The `main.php` script relies on `[api:Director]` to work out which [controller](../controllers) should handle this request. It parses the URL, matching it to one of a number of patterns,
The `main.php` script relies on `[api:Director]` to work out which [controller](../controllers/) should handle this request. It parses the URL, matching it to one of a number of patterns,
and determines the controller, action and any argument to be used ([Routing](../controllers/routing)).
* Creates a `[api:SS_HTTPRequest]` object containing all request and environment information
* The [session](../cookies_and_sessions/sessions) holds an abstraction of PHP session
* Instantiates a [controller](../Controllers) object
* Instantiates a [controller](../controllers/) object
* The `[api:Injector]` is first referenced, and asks the registered
[RequestFilter](../controller/request_filters)
[RequestFilter](../controllers/requestfilters)
to pre-process the request object (see below)
* The `Controller` executes the actual business logic and populates an `[api:SS_HTTPResponse]`
* The `Controller` can optionally hand off control to further nested controllers
@ -125,7 +125,7 @@ The framework provides the ability to hook into the request both before and
after it is handled to allow binding custom logic. This can be used
to transform or filter request data, instanciate helpers, execute global logic,
or even short-circuit execution (e.g. to enforce custom authentication schemes).
The ["Request Filters" documentation](../controller/request_filters) shows you how.
The ["Request Filters" documentation](../controllers/requestfilters) shows you how.
## Flushing Manifests

View File

@ -35,14 +35,14 @@ Thanks to Rutger de Jong for reporting.
Severity: Moderate
Autologin tokens (remember me and reset password) are stored in the database as a plain text.
If attacker obtained the database he would be able to gain access to accounts that have requested a password change, or have "remember me" enabled.
If attacker obtained the database they would be able to gain access to accounts that have requested a password change, or have "remember me" enabled.
### Security: Privilege escalation through profile form
Severity: Moderate
A logged-in CMS user can gain additional privileges by crafting a request
to his/her profile form which resets another user's password.
to their profile form which resets another user's password.
This method can potentially be used by CSRF attacks as well.
Thanks to Nathaniel Carew (Sense of Security) for reporting.

View File

@ -607,9 +607,7 @@ when using deprecated functionality (through the new `Deprecation` class).
* 2012-04-12 [e9dc610](https://github.com/silverstripe/sapphire/commit/e9dc610) API-CHANGE: new GridFieldFooter component (Julian Seidenberg)
* 2012-04-10 [9888f98](https://github.com/silverstripe/silverstripe-cms/commit/9888f98) ENHANCMENT: Link pages in reports to cms edit (Andrew O'Neil)
* 2012-04-10 [1516934](https://github.com/silverstripe/silverstripe-cms/commit/1516934) Revert "BUGFIX: SSF-168 fixing rendering issue in Chrome, which displays extra control at the bottom of the window in a report that is of a certain length" (Julian Seidenberg)
* 2012-04-06 [797d526](https://github.com/silverstripe/sapphire/commit/797d526) For png images with transparency, the imagesaveaplpha() needs to be set to true on the source image in order for
he alpha to be preserved when using the modifier methods. (jmwohl)
* 2012-04-06 [797d526](https://github.com/silverstripe/sapphire/commit/797d526) For png images with transparency, the imagesaveaplpha() needs to be set to true on the source image in order for the alpha to be preserved when using the modifier methods. (jmwohl)
* 2012-04-05 [e76913f](https://github.com/silverstripe/sapphire/commit/e76913f) API-CHANGE: adding a default option of null to the $args argument in DataExtension::add_to_class. The args argument isn't used anywhere in the class and adding a third argument to every call to this function is tedious. (Julian Seidenberg)
* 2012-04-04 [5826b36](https://github.com/silverstripe/sapphire/commit/5826b36) ENHACEMENT: SSF-168 updated the font for titles on print stylesheets (Felipe Skroski)
* 2012-04-04 [349a04d](https://github.com/silverstripe/silverstripe-cms/commit/349a04d) API-CHANGE: SSF-168 changing the API/code-conventions for excluding specific reports. get_reports method now returns an ArrayList instead of an array of SS_Reports. (Julian Seidenberg)

View File

@ -22,7 +22,7 @@ Thanks to Rutger de Jong for reporting.
Severity: Moderate
A logged-in CMS user can gain additional privileges by crafting a request
to his/her profile form which resets another user's password.
to their profile form which resets another user's password.
This method can potentially be used by CSRF attacks as well.
Thanks to Nathaniel Carew (Sense of Security) for reporting.

View File

@ -25,7 +25,7 @@ API changes related to the below security patch:
Severity: Moderate
Autologin tokens (remember me and reset password) are stored in the database as a plain text.
If attacker obtained the database he would be able to gain access to accounts that have requested a password change, or have "remember me" enabled.
If attacker obtained the database they would be able to gain access to accounts that have requested a password change, or have "remember me" enabled.
## Changelog

View File

@ -0,0 +1,28 @@
# 3.1.11-rc1
# Overview
This release contains fixes to resolve issues in 3.1.10 running in a Suhosin environment.
Failure to update may result in unpredictable behaviour of the GridField control in affected sites.
### Bugfixes
* 2015-03-09 [1770fab](https://github.com/silverstripe/sapphire/commit/1770fab) Fix gridfield generating invalid session keys (Damian Mooyman)
* 2015-03-05 [87adc44](https://github.com/silverstripe/sapphire/commit/87adc44) Fix serialised stateid exceeding request length (Damian Mooyman)
* 2015-03-04 [eb35f26](https://github.com/silverstripe/sapphire/commit/eb35f26) Corrected padding on non-sortable columns. (Sam Minnee)
* 2015-03-03 [6e0afd5](https://github.com/silverstripe/sapphire/commit/6e0afd5) Prevent unnecessary call to config system which doesn't exist yet (micmania1)
* 2015-03-03 [4709b90](https://github.com/silverstripe/sapphire/commit/4709b90) UploadField description alignment (Loz Calver)
* 2015-03-02 [f234301](https://github.com/silverstripe/sapphire/commit/f234301) DataQuery::applyRelation using incorrect foreign key (fixes #3954) (Loz Calver)
* 2015-03-02 [f9d493d](https://github.com/silverstripe/sapphire/commit/f9d493d) Fixes case insensitive search for postgres databases (Jean-Fabien Barrois)
* 2015-02-27 [4c5a07e](https://github.com/silverstripe/sapphire/commit/4c5a07e) Updated docs (Michael Strong)
* 2015-02-25 [3a7e24a](https://github.com/silverstripe/sapphire/commit/3a7e24a) Unable to access a list of all many_many_extraFields (Loz Calver)
* 2015-02-13 [998c055](https://github.com/silverstripe/sapphire/commit/998c055) Misleading error message in SSViewer (Loz Calver)
* 2015-02-10 [bbe2799](https://github.com/silverstripe/sapphire/commit/bbe2799) Use correct query when searching for items managed by a tree dropdown field #3173 (Jean-Fabien Barrois)
* 2015-01-13 [ab24ed3](https://github.com/silverstripe/sapphire/commit/ab24ed3) . Use i18n_plural_name() instead of plural_name() (Elvinas L.)
* 2014-11-17 [a142ffd](https://github.com/silverstripe/silverstripe-cms/commit/a142ffd) VirtualPages use correct casting for 'virtual' database fields (Loz Calver)
## Changelog
* [framework](https://github.com/silverstripe/silverstripe-framework/releases/tag/3.1.11-rc1)
* [cms](https://github.com/silverstripe/silverstripe-cms/releases/tag/3.1.11-rc1)
* [installer](https://github.com/silverstripe/silverstripe-installer/releases/tag/3.1.11-rc1)

View File

@ -6,7 +6,7 @@ The SilverStripe core modules (`framework` and `cms`), as well as some of the mo
git version control. SilverStripe hosts its modules on [github.com/silverstripe](http://github.com/silverstripe) and [github.com/silverstripe-labs](http://github.com/silverstripe-labs). After [installing git](http://help.github.com/git-installation-redirect) and creating a [free github.com account](https://github.com/signup/free), you can "fork" a module,
which creates a copy that you can commit to (see github's [guide to "forking"](http://help.github.com/forking/)).
For other modules, our [module list on silverstripe.org](http://silverstripe.org/modules) lists the repository locations, typically using a version control system like "git" or "[subversion](subversion)".
For other modules, our [add-ons site](http://addons.silverstripe.org/add-ons) lists the repository locations, typically using the version control system like "git".
<div class="hint" markdown="1">
Note: By supplying code to the SilverStripe core team in patches, tickets and pull requests, you agree to assign copyright of that code to SilverStripe Limited, on the condition that SilverStripe Limited releases that code under the BSD license.
@ -16,9 +16,10 @@ We ask for this so that the ownership in the license is clear and unambiguous, a
## Step-by-step: From forking to sending the pull request
_**NOTE:** The commands on this page assume that you are branching from `3.2`, at the time of writing this is the pre-release branch._
_**NOTE:** The commands on this page assume that you are targetting framework version 3.2
1. Install the project through composer. The process is described in detail in "[Installation through Composer](../getting_started/composer#contributing)".
1. Install the project through composer. The process is described in detail in "[Installation through Composer](../../installation/composer#contributing)".
composer create-project --keep-vcs --dev silverstripe/installer ./my/website/folder 3.2.x-dev
@ -63,7 +64,43 @@ _**NOTE:** The commands on this page assume that you are branching from `3.2`, a
8. Issue pull request on GitHub. Visit your forked respoistory on GitHub.com and click the "Create Pull Request" button nex tot the new branch.
The core team is then responsible for reviewing patches and deciding if they will make it into core. If
there are any problems they will follow up with you, so please ensure they have a way to contact you!
there are any problems they will follow up with you, so please ensure they have a way to contact you!
### The Pull Request Process
Once your pull request is issued, it's not the end of the road. A [core committer](/contributing/core_committers/) will most likely have some questions for you and may ask you to make some changes depending on discussions you have.
If you've been naughty and not adhered to the coding conventions, expect a few requests to make changes so your code is in-line.
If your change is particularly significant, it may be referred to the [mailing list](https://groups.google.com/forum/#!forum/silverstripe-dev) for further community discussion.
A core committer will also "label" your PR using the labels defined in GitHub, these are to correctly classify and help find your work at a later date.
#### GitHub Labels
The current GitHub labels are grouped into 5 sections:
1. Changes - These are designed to signal what kind of change they are and how they fit into the [Semantic Versioning](http://semver.org/) schema
2. Impact - What impact does this bug/issue/fix have, does it break a feature completely, is it just a side effect or is it trivial and not a bit problem (but a bit annoying)
3. Effort - How much effort is required to fix this issue?
4. Type - What aspect of the system the PR/issue covers
5. Feedback - Are we waiting on feedback, if so who from? Typically used for issues that are likely to take a while to have feedback given
| Label | Purpose |
| ----- | ------- |
| change/major | A change for the next major release (eg: 4.0) |
| change/minor | A change for the next minor release (eg: 3.x) |
| change/patch | A change for the next patch release (eg: 3.1.x) |
| impact/critical | Broken functionality for which no work around can be produced |
| impact/high | Broken functionality but can be mitigated by other non-core code changes |
| impact/medium | Unexpected behaviour but does not break functionality |
| impact/low | A nuisance but doesn't break any functionality (typos, etc) |
| effort/easy | Someone with limited SilverStripe experience could resolve |
| effort/medium | Someone with a good understanding of SilverStripe could resolve |
| effort/hard | Only an expert with SilverStripe could resolve |
| type/docs | A docs change |
| type/frontend | A change to front-end (CSS, HTML, etc) |
| feedback-required/core-team | Core team members need to give an in-depth consideration |
| feedback-required/author | This issue is awaiting feedback from the original author of the PR |
### Workflow Diagram ###
@ -75,7 +112,7 @@ If you aren't familiar with git and GitHub, try reading the ["GitHub bootcamp do
We also found the [free online git book](http://git-scm.com/book/) and the [git crash course](http://gitref.org/) useful.
If you're familiar with it, here's the short version of what you need to know. Once you fork and download the code:
* **Don't develop on the master branch.** Always create a development branch specific to "the issue" you're working on (mostly on our [bugtracker](/misc/contributing/issues)). Name it by issue number and description. For example, if you're working on Issue #100, a `DataObject::get_one()` bugfix, your development branch should be called 100-dataobject-get-one. If you decide to work on another issue mid-stream, create a new branch for that issue--don't work on both in one branch.
* **Don't develop on the master branch.** Always create a development branch specific to "the issue" you're working on (on our [GitHub repository's issues](https://github.com/silverstripe/silverstripe-framework/issues)). Name it by issue number and description. For example, if you're working on Issue #100, a `DataObject::get_one()` bugfix, your development branch should be called 100-dataobject-get-one. If you decide to work on another issue mid-stream, create a new branch for that issue--don't work on both in one branch.
* **Do not merge the upstream master** with your development branch; *rebase* your branch on top of the upstream master.
@ -86,7 +123,7 @@ If you're familiar with it, here's the short version of what you need to know. O
* **Choose the correct branch**: Assume the current release is 3.0.3, and 3.1.0 is in beta state.
Most pull requests should go against the `3.1.x-dev` *pre-release branch*, only critical bugfixes
against the `3.0.x-dev` *release branch*. If you're changing an API or introducing a major feature,
the pull request should go against `master` (read more about our [release process](/misc/release-process)). Branches are periodically merged "upwards" (3.0 into 3.1, 3.1 into master).
the pull request should go against `master` (read more about our [release process](release_process)). Branches are periodically merged "upwards" (3.0 into 3.1, 3.1 into master).
### Editing files directly on GitHub.com
@ -96,11 +133,11 @@ After you have edited the file, GitHub will offer to create a pull request for y
## Check List
* Adhere to our [coding conventions](/misc/coding-conventions)
* Adhere to our [coding conventions](/getting_started/coding_conventions)
* If your patch is extensive, discuss it first on the [silverstripe-dev google group](https://groups.google.com/group/silverstripe-dev) (ideally before doing any serious coding)
* When working on existing tickets, provide status updates through ticket comments
* Check your patches against the "master" branch, as well as the latest release branch
* Write [unit tests](/topics/testing)
* Write [unit tests](../developer_guides/testing/unit_testing)
* Write [Behat integration tests](https://github.com/silverstripe-labs/silverstripe-behat-extension) for any interface changes
* Describe specifics on how to test the effects of the patch
* It's better to submit multiple patches with separate bits of functionality than a big patch containing lots of
@ -110,7 +147,7 @@ changes
[API documentation](http://api.silverstripe.org/3.1/) for good examples.
* Check and update documentation on [doc.silverstripe.org](http://doc.silverstripe.org). Check for any references to functionality deprecated or extended through your patch. Documentation changes should be included in the patch.
* If you get stuck, please post to the [forum](http://silverstripe.org/forum) or for deeper core problems, to the [core mailinglist](https://groups.google.com/forum/#!forum/silverstripe-dev)
* When working with the CMS, please read the ["CMS Architecture Guide"](/reference/cms-architecture) first
* When working with the CMS, please read the ["CMS Architecture Guide"](cms_architecture) first
## Commit Messages

View File

@ -9,7 +9,7 @@ only have time for a partial translation or quick review work - our system accom
same language.
The content for UI elements (button labels, field titles) and instruction texts shown in the CMS and elsewhere is
stored in the PHP code for a module (see [i18n](/topics/i18n)). All content can be extracted as a "language file", and
stored in the PHP code for a module (see [i18n](../developer_guides/i18n)). All content can be extracted as a "language file", and
uploaded to an online translation editor interface. SilverStripe is already translated in over 60 languages, and we're
relying on native speakers to keep these up to date, and of course add new languages.
@ -83,13 +83,13 @@ dropdown which automatically includes all found translations (based on the files
### I've found a piece of untranslatable text
It is entirely possible that we missed certain strings in preparing Silverstripe for translation-support. If you're
technically minded, please read [i18n](/topics/i18n) on how to make it translatable. Otherwise just post your findings
technically minded, please read [i18n](../developer_guides/i18n) on how to make it translatable. Otherwise just post your findings
to the forum.
### How do I add my own module?
Once you've built a translation-enabled module, you can run the "textcollector" on your local installation for this
specific module (see [i18n](/topics/i18n)). This should find all calls to `_t()` in php and template files, and generate
specific module (see [i18n](../developer_guides/i18n)). This should find all calls to `_t()` in php and template files, and generate
a new lang file with the default locale (path: <mymodule>/lang/en.yml). Upload this file to the online translation
tool, and wait for your translators to do their magic!
@ -119,7 +119,7 @@ translators.
### I'm seeing lots of duplicated translations, what should I do?
For now, please translate all duplications - sometimes they might be intentional, but mostly the developer just didn't
know his phrase was already translated. Please contact us about any duplicates that might be worth merging.
know their phrase was already translated. Please contact us about any duplicates that might be worth merging.
### What happened to translate.silverstripe.org?
@ -128,7 +128,7 @@ This was a custom-built online translation tool serving us well for a couple of
were migrated. Unfortunately, the ownership of individual translations couldn't be migrated.
As the new tool doesn't support the PHP format used in SilverStripe 2.x, this means that we no longer have a working
translation tool for PHP files. Please edit the PHP files directly and [send us pull requests](/misc/contributing).
translation tool for PHP files. Please edit the PHP files directly and [send us pull requests](/contributing).
This also applies for any modules staying compatible with SilverStripe 2.x.
@ -140,7 +140,7 @@ board if you have specific comments on a translation.
## Related
* [i18n](/developer_guids/i18n): Developer-level documentation of Silverstripe's i18n capabilities
* [translation-process](translation-process): Information about managing translations for the core team and/or module maintainers.
* [i18n](/developer_guides/i18n): Developer-level documentation of Silverstripe's i18n capabilities
* [Translation Process](translation_process): Information about managing translations for the core team and/or module maintainers.
* [translatable](https://github.com/silverstripe/silverstripe-translatable): DataObject-interface powering the website-content translations
* ["Translatable ModelAdmin" module](http://silverstripe.org/translatablemodeladmin-module/): An extension which allows translations of DataObjects inside ModelAdmin

View File

@ -4,14 +4,14 @@ summary: Implement SilverStripe's internationalization system in your own module
# Implementing Internationalization
To find out about how to assist with translating SilverStripe from a users point of view, see the
[Contributing Translations page](translation).
[Contributing Translations page](/contributing/translations).
## Set up your own module for localization
### Collecting translatable text
As a first step, you can automatically collect all translatable text in your module through the `i18nTextCollector`
task. See [i18n](/topics/i18n#collecting-text) for more details.
task. See [i18n](../developer_guides/i18n#collecting-text) for more details.
### Import master files
@ -127,7 +127,7 @@ files back into the JS files SilverStripe can actually read. This requires an in
# Related
* [i18n](/topics/i18n): Developer-level documentation of Silverstripe's i18n capabilities
* [contributing/translation](contributing/translation): Information for translators looking to contribute translations of the SilverStripe UI.
* [i18n](/developer_guides/i18n/): Developer-level documentation of Silverstripe's i18n capabilities
* [Contributing Translations](/contributing/translations): Information for translators looking to contribute translations of the SilverStripe UI.
* [translatable](https://github.com/silverstripe/silverstripe-translatable): DataObject-interface powering the website-content translations
* ["Translatable ModelAdmin" module](http://silverstripe.org/translatablemodeladmin-module/): An extension which allows translations of DataObjects inside ModelAdmin

View File

@ -24,7 +24,7 @@ With great power comes great responsibility, so we have agreed on certain expect
* Treat issues according to our [issue guidelines](issues_and_bugs)
* Don't commit directly to core, raise pull requests instead (except trivial fixes)
* Only merge code you have tested and fully understand. If in doubt, ask for a second opinion.
* Ensure contributions have appropriate [test coverage](/topics/testing), are documented, and pass our [coding conventions](/getting_started/coding-conventions)
* Ensure contributions have appropriate [test coverage](../developer_guides/testing), are documented, and pass our [coding conventions](/getting_started/coding_conventions)
* Keep the codebase "releasable" at all times (check our [release process](release_process))
* API changes and non-trivial features should not be merged into release branches.
* API changes on master should not be merged until they have the buy-in of at least two core committers (or better, through the [core mailing list](https://groups.google.com/forum/#!forum/silverstripe-dev))

View File

@ -830,7 +830,7 @@ class File extends DataObject {
'js' => _t('File.JsType', 'Javascript file'),
'css' => _t('File.CssType', 'CSS file'),
'html' => _t('File.HtmlType', 'HTML file'),
'htm' => _t('File.HtlType', 'HTML file')
'htm' => _t('File.HtmlType', 'HTML file')
);
$ext = $this->getExtension();

View File

@ -42,14 +42,14 @@ class ImagickBackend extends Imagick implements Image_Backend {
/**
* set_default_quality
*
* @deprecated 3.2 Use the "IMagickBackend.default_quality" config setting instead
* @deprecated 3.2 Use the "ImagickBackend.default_quality" config setting instead
* @param int $quality
* @return void
*/
public static function set_default_quality($quality) {
Deprecation::notice('3.2', 'Use the "IMagickBackend.default_quality" config setting instead');
Deprecation::notice('3.2', 'Use the "ImagickBackend.default_quality" config setting instead');
if(is_numeric($quality) && (int) $quality >= 0 && (int) $quality <= 100) {
config::inst()->update('IMagickBackend', 'default_quality', (int) $quality);
Config::inst()->update('ImagickBackend', 'default_quality', (int) $quality);
}
}

View File

@ -453,7 +453,6 @@ class TreeDropdownField extends FormField {
$this->labelField
));
}
$res = DataObject::get($this->sourceObject)->filterAny($filters);
}
@ -463,18 +462,18 @@ class TreeDropdownField extends FormField {
if ($row->ParentID) $parents[$row->ParentID] = true;
$this->searchIds[$row->ID] = true;
}
$sourceObject = $this->sourceObject;
while (!empty($parents)) {
$idsClause = DB::placeholders($parents);
$res = DB::prepared_query(
"SELECT \"ParentID\", \"ID\" FROM \"{$this->sourceObject}\" WHERE \"ID\" in ($idsClause)",
array_keys($parents)
);
$items = $sourceObject::get()
->filter("ID",array_keys($parents));
$parents = array();
foreach($res as $row) {
if ($row['ParentID']) $parents[$row['ParentID']] = true;
$this->searchIds[$row['ID']] = true;
$this->searchExpanded[$row['ID']] = true;
foreach($items as $item) {
if ($item->ParentID) $parents[$item->ParentID] = true;
$this->searchIds[$item->ID] = true;
$this->searchExpanded[$item->ID] = true;
}
}
}

View File

@ -841,7 +841,8 @@ class GridField_FormAction extends FormAction {
'args' => $this->args,
);
$id = md5(serialize($state));
// Ensure $id doesn't contain only numeric characters
$id = 'gf_'.substr(md5(serialize($state)), 0, 8);
Session::set($id, $state);
$actionData['StateID'] = $id;

View File

@ -95,7 +95,7 @@ class GridFieldDetailForm implements GridField_URLHandler {
// if no validator has been set on the GridField and the record has a
// CMS validator, use that.
if(!$this->getValidator() && method_exists($record, 'getCMSValidator')) {
if(!$this->getValidator() && (method_exists($record, 'getCMSValidator') || $record instanceof Object && $record->hasMethod('getCMSValidator'))) {
$this->setValidator($record->getCMSValidator());
}

View File

@ -632,9 +632,13 @@ class i18nTextCollector_Writer_RailsYaml implements i18nTextCollector_Writer {
}
// Write YAML
$oldVersion = sfYaml::getSpecVersion();
sfYaml::setSpecVersion('1.1');
$yamlHandler = new sfYaml();
// TODO Dumper can't handle YAML comments, so the context information is currently discarded
return $yamlHandler->dump(array($locale => $entitiesNested), 99);
$result = $yamlHandler->dump(array($locale => $entitiesNested), 99);
sfYaml::setSpecVersion($oldVersion);
return $result;
}
}

41
javascript/lang/src/sv.js Normal file
View File

@ -0,0 +1,41 @@
{
"VALIDATOR.FIELDREQUIRED": "Var god fyll i \"%s\", det är obligatoriskt.",
"HASMANYFILEFIELD.UPLOADING": "Laddar upp... %s",
"TABLEFIELD.DELETECONFIRMMESSAGE": "Vill du verkligen radera detta?",
"LOADING": "laddar...",
"UNIQUEFIELD.SUGGESTED": "Ändrade värde till '%s' : %s",
"UNIQUEFIELD.ENTERNEWVALUE": "Du måste fylla i ett nytt värde för detta fält",
"UNIQUEFIELD.CANNOTLEAVEEMPTY": "Detta fält kan inte lämnas tomt",
"RESTRICTEDTEXTFIELD.CHARCANTBEUSED": "Tecknet '%s' kan inte användas i detta fält",
"UPDATEURL.CONFIRM": "Vill du att URL:en ändras till:\n\n%s/\n\nKlicka OK för att ändra URL:en, klicka Avbryt för att lämna den som:\n\n%s",
"UPDATEURL.CONFIRMURLCHANGED": "URL:en har ändrats till\n'%s'",
"FILEIFRAMEFIELD.DELETEFILE": "Radera fil",
"FILEIFRAMEFIELD.UNATTACHFILE": "Avlänka fil",
"FILEIFRAMEFIELD.DELETEIMAGE": "Radera bild",
"FILEIFRAMEFIELD.CONFIRMDELETE": "Vill du verkligen radera denna fil?",
"LeftAndMain.IncompatBrowserWarning": "Din webbläsare är inte kompatibel med detta CMS. Var god använd Internet Explorer 7+, Google Chrome 10+ eller Mozilla Firefox 3.5+.",
"GRIDFIELD.ERRORINTRANSACTION": "Ett fel uppstod när data hämtades från servern.\nVar god försök igen senare.",
"HtmlEditorField.SelectAnchor": "Välj ett ankare",
"UploadField.ConfirmDelete": "Är du säker på att du vill radera denna fil från servern?",
"UploadField.PHP_MAXFILESIZE": "Filen överskrider upload_max_filesize (php-ini-direktiv)",
"UploadField.HTML_MAXFILESIZE": "Filen överskrider MAX_FILE_SIZE (HTML form-direktiv)",
"UploadField.ONLYPARTIALUPLOADED": "Filen laddas bara upp delvis",
"UploadField.NOFILEUPLOADED": "Ingen fil laddades upp",
"UploadField.NOTMPFOLDER": "Tillfällig mapp saknas",
"UploadField.WRITEFAILED": "Kunde inte skriva filen",
"UploadField.STOPEDBYEXTENSION": "Uppladdning stoppades av otillåten filtyp",
"UploadField.TOOLARGE": "Filen är för stor",
"UploadField.TOOSMALL": "Filen är för liten",
"UploadField.INVALIDEXTENSION": "Filtypen tillåts inte",
"UploadField.MAXNUMBEROFFILESSIMPLE": "Maximalt antal filer överstiget",
"UploadField.UPLOADEDBYTES": "Antalet uppladdade bytes överstiger filstorleken",
"UploadField.EMPTYRESULT": "Tomt uppladdningsresultat",
"UploadField.LOADING": "Laddar ...",
"UploadField.Editing": "Redigerar ...",
"UploadField.Uploaded": "Uppladdad",
"UploadField.OVERWRITEWARNING": "Fil med samma namn existerar redan",
"TreeDropdownField.ENTERTOSEARCH": "Tryck Enter för att söka",
"TreeDropdownField.OpenLink": "Öppna",
"TreeDropdownField.FieldTitle": "Välj",
"TreeDropdownField.SearchFieldTitle": "Välj eller Sök"
}

47
javascript/lang/sv.js Normal file
View File

@ -0,0 +1,47 @@
// This file was generated by GenerateJavaScriptI18nTask from javascript/lang/src/sv.js.
// See https://github.com/silverstripe/silverstripe-buildtools for details
if(typeof(ss) == 'undefined' || typeof(ss.i18n) == 'undefined') {
if(typeof(console) != 'undefined') console.error('Class ss.i18n not defined');
} else {
ss.i18n.addDictionary('sv', {
"VALIDATOR.FIELDREQUIRED": "Var god fyll i \"%s\", det är obligatoriskt.",
"HASMANYFILEFIELD.UPLOADING": "Laddar upp... %s",
"TABLEFIELD.DELETECONFIRMMESSAGE": "Vill du verkligen radera detta?",
"LOADING": "laddar...",
"UNIQUEFIELD.SUGGESTED": "Ändrade värde till '%s' : %s",
"UNIQUEFIELD.ENTERNEWVALUE": "Du måste fylla i ett nytt värde för detta fält",
"UNIQUEFIELD.CANNOTLEAVEEMPTY": "Detta fält kan inte lämnas tomt",
"RESTRICTEDTEXTFIELD.CHARCANTBEUSED": "Tecknet '%s' kan inte användas i detta fält",
"UPDATEURL.CONFIRM": "Vill du att URL:en ändras till:\n\n%s/\n\nKlicka OK för att ändra URL:en, klicka Avbryt för att lämna den som:\n\n%s",
"UPDATEURL.CONFIRMURLCHANGED": "URL:en har ändrats till\n'%s'",
"FILEIFRAMEFIELD.DELETEFILE": "Radera fil",
"FILEIFRAMEFIELD.UNATTACHFILE": "Avlänka fil",
"FILEIFRAMEFIELD.DELETEIMAGE": "Radera bild",
"FILEIFRAMEFIELD.CONFIRMDELETE": "Vill du verkligen radera denna fil?",
"LeftAndMain.IncompatBrowserWarning": "Din webbläsare är inte kompatibel med detta CMS. Var god använd Internet Explorer 7+, Google Chrome 10+ eller Mozilla Firefox 3.5+.",
"GRIDFIELD.ERRORINTRANSACTION": "Ett fel uppstod när data hämtades från servern.\nVar god försök igen senare.",
"HtmlEditorField.SelectAnchor": "Välj ett ankare",
"UploadField.ConfirmDelete": "Är du säker på att du vill radera denna fil från servern?",
"UploadField.PHP_MAXFILESIZE": "Filen överskrider upload_max_filesize (php-ini-direktiv)",
"UploadField.HTML_MAXFILESIZE": "Filen överskrider MAX_FILE_SIZE (HTML form-direktiv)",
"UploadField.ONLYPARTIALUPLOADED": "Filen laddas bara upp delvis",
"UploadField.NOFILEUPLOADED": "Ingen fil laddades upp",
"UploadField.NOTMPFOLDER": "Tillfällig mapp saknas",
"UploadField.WRITEFAILED": "Kunde inte skriva filen",
"UploadField.STOPEDBYEXTENSION": "Uppladdning stoppades av otillåten filtyp",
"UploadField.TOOLARGE": "Filen är för stor",
"UploadField.TOOSMALL": "Filen är för liten",
"UploadField.INVALIDEXTENSION": "Filtypen tillåts inte",
"UploadField.MAXNUMBEROFFILESSIMPLE": "Maximalt antal filer överstiget",
"UploadField.UPLOADEDBYTES": "Antalet uppladdade bytes överstiger filstorleken",
"UploadField.EMPTYRESULT": "Tomt uppladdningsresultat",
"UploadField.LOADING": "Laddar ...",
"UploadField.Editing": "Redigerar ...",
"UploadField.Uploaded": "Uppladdad",
"UploadField.OVERWRITEWARNING": "Fil med samma namn existerar redan",
"TreeDropdownField.ENTERTOSEARCH": "Tryck Enter för att söka",
"TreeDropdownField.OpenLink": "Öppna",
"TreeDropdownField.FieldTitle": "Välj",
"TreeDropdownField.SearchFieldTitle": "Välj eller Sök"
});
}

View File

@ -235,7 +235,7 @@ lt:
SINGULARNAME: Grupė
Sort: 'Rūšiavimo tvarka'
has_many_Permissions: Leidimai
many_many_Members: Nariai
many_many_Members: Vartotojai
GroupImportForm:
Help1: '<p>Importuoti vieną ar kelias grupes <em>CSV</em> formatu (kableliu atskirtos reikšmės). <small><a href="#" class="toggle-advanced">Rodyti detalesnį aprašymą</a></small></p>'
Help2: "<div class=\"advanced\">\n<h4>Detalesnis aprašymas</h4>\n<ul>\n<li>Galimi stulpeliai: <em>%s</em></li>\n<li>Esamos grupės yra surišamos su jų unikalia <em>Code</em> reikšme ir atnaujinamos duomenimis iš importuojamos bylos</li>\n<li>Grupių hierarchija gali būti sukurta naudojant <em>ParentCode</em> stulpelį.</li>\n<li>Leidimų gali būti priskirti naudojant <em>PermissionCode</em> stulpelį. Esami leidimai nebus pakeisti.</li>\n</ul>\n</div>"
@ -350,15 +350,15 @@ lt:
NEWPASSWORD: 'Naujas slaptažodis'
NoPassword: 'Šis vartotojas neturi slaptažodžio.'
PASSWORD: Slaptažodis
PLURALNAME: Nariai
PLURALNAME: Vartotojai
REMEMBERME: 'Prisiminti jungiantis kitą kartą?'
SINGULARNAME: Narys
SINGULARNAME: Vartotojas
SUBJECTPASSWORDCHANGED: 'Jūsų slaptažodis pakeistas'
SUBJECTPASSWORDRESET: 'Slaptažodžio atstatymo nuoroda'
SURNAME: Pavardė
TIMEFORMAT: 'Laiko formatas'
VALIDATIONMEMBEREXISTS: 'Tokį e. paštą jau naudoja kitas narys.'
ValidationIdentifierFailed: 'Nepavyko perrašyti nario #{id} su tuo pačiu atpažinimo kodu ({name} = {value}))'
VALIDATIONMEMBEREXISTS: 'Vartotojas šiuo el. pašto adresu %s jau egzistuoja.'
ValidationIdentifierFailed: 'Nepavyko atnaujinti vartotojo #{id} duomenų su atpažinimo kodu ({name} = {value})'
WELCOMEBACK: 'Sveiki, {firstname}'
YOUROLDPASSWORD: 'Jūsų senas slaptažodis'
belongs_many_many_Groups: Grupės
@ -488,7 +488,7 @@ lt:
GROUPNAME: 'Grupės pavadinimas'
IMPORTGROUPS: 'Importuoti grupes'
IMPORTUSERS: 'Importuoti vartotojus'
MEMBERS: Nariai
MEMBERS: Vartotojai
MENUTITLE: Saugumas
MemberListCaution: 'Dėmesio: Pašalinus vartotojus iš šio sąrašo, jie bus pašalinti ir iš visų grupių, bei duomenų bazės.'
NEWGROUP: 'Nauja grupė'

View File

@ -1,6 +1,7 @@
sv:
AssetAdmin:
NEWFOLDER: Ny mapp
SHOWALLOWEDEXTS: 'Visa tillåtna filtyper'
AssetTableField:
CREATED: 'Först uppladdad'
DIM: Dimensioner
@ -67,6 +68,8 @@ sv:
ACCESSALLINTERFACES: 'Tillgång till alla CMS-sektioner'
ACCESSALLINTERFACESHELP: 'Ersätter mer specifika behörighetsinställningar.'
SAVE: Spara
CMSPageHistoryController_versions_ss:
PREVIEW: 'Förhandsgranska sida'
CMSProfileController:
MENUTITLE: 'Min Profil'
ChangePasswordEmail_ss:
@ -87,6 +90,8 @@ sv:
FOURTH: fjärde
SECOND: andra
THIRD: tredje
CurrencyField:
CURRENCYSYMBOL: $
DataObject:
PLURALNAME: 'Dataobjekt'
SINGULARNAME: 'Dataobjekt'
@ -96,9 +101,12 @@ sv:
HOUR: timme
HOURS: timmar
LessThanMinuteAgo: 'mindre än en minut'
MIN: minut
MINS: minuter
MONTH: månad
MONTHS: månader
SEC: sek
SEC: sekund
SECS: sekunder
TIMEDIFFAGO: '{difference} sen'
TIMEDIFFIN: 'om {difference}'
YEAR: år
@ -121,37 +129,39 @@ sv:
Enum:
ANY: Vilken som helst
File:
AviType: 'AVI videofil'
AviType: 'AVI-videofil'
Content: Innehåll
CssType: 'CSS fil'
DmgType: 'Apple skivavbild'
DocType: 'Word dokument'
CssType: 'CSS-fil'
DmgType: 'Apple-skivavbild'
DocType: 'Word-dokument'
Filename: Filnamn
GifType: 'GIF bild - bra för diagram'
GzType: 'GZIP packad fil'
HtlType: 'HTML fil'
HtmlType: 'HTML fil'
GifType: 'GIF-bild - bra för diagram'
GzType: 'GZIP-packad fil'
HtlType: 'HTML-fil'
HtmlType: 'HTML-fil'
INVALIDEXTENSION: 'Filändelsen tillåts inte (tillåtna: {extensions})'
INVALIDEXTENSIONSHORT: 'Filändelsen tillåts inte'
IcoType: 'Icon bild'
JpgType: 'JPEG bild - bra för fotografier'
JsType: 'Javascript fil'
Mp3Type: 'MP3 ljudfil'
MpgType: 'MPEG videofil'
IcoType: 'Ikonbild'
JpgType: 'JPEG-bild - bra för fotografier'
JsType: 'Javascript-fil'
Mp3Type: 'MP3-ljudfil'
MpgType: 'MPEG-videofil'
NOFILESIZE: 'Filstorleken är noll bytes'
NOVALIDUPLOAD: 'Filen är inte giltig för uppladdning'
Name: Namn
PLURALNAME: Filer
PdfType: 'Adobe Acrobat PDF fil'
PngType: 'PNG bild - bra allmänt format'
PdfType: 'Adobe Acrobat PDF-fil'
PngType: 'PNG-bild - bra allmänt format'
SINGULARNAME: Fil
TOOLARGE: 'Filen är för stor, max {size} tillåts'
TOOLARGESHORT: 'Filstorlek överskriden {size}'
TiffType: 'Tiff bildformat'
Title: Titel
WavType: 'WAV ljudfil'
WavType: 'WAV-ljudfil'
XlsType: 'Excel kalkylblad'
ZipType: 'ZIP packad fil'
ZipType: 'ZIP-packad fil'
Filesystem:
SYNCRESULTS: 'Synkning komplett: {createdcount} artiklar skapades, {deletedcount} artiklar raderades'
Folder:
PLURALNAME: Mappar
SINGULARNAME: Mapp
@ -161,14 +171,19 @@ sv:
TEXT2: 'Återställningslänk för lösenord'
TEXT3: för
Form:
CSRF_FAILED_MESSAGE: "Ett tekniskt fel uppstod. Var god klicka på bakåt-knappen,\n⇥⇥⇥⇥⇥ladda om webbläsaren och försök igen."
FIELDISREQUIRED: '{name} är obligatoriskt'
SubmitBtnLabel: Kör
VALIDATIONCREDITNUMBER: 'Kontrollera att du angav kortnummret {number} rätt'
VALIDATIONNOTUNIQUE: 'Det angivna värdet är inte unikt'
VALIDATIONPASSWORDSDONTMATCH: 'Lösenorden stämmer inte överrens '
VALIDATIONPASSWORDSNOTEMPTY: 'Lösenordfältet får inte vara tomt'
VALIDATIONSTRONGPASSWORD: 'Lösenord måste innehålla minst en siffra och en bokstav.'
VALIDATOR: Validator
VALIDCURRENCY: 'Var vänlig ange en korrekt valuta'
CSRF_EXPIRED_MESSAGE: 'Din session har upphört. Var god och skicka in formuläret på nytt.'
FormField:
Example: 't.ex. %s'
NONE: ingen
GridAction:
DELETE_DESCRIPTION: Radera
@ -191,6 +206,7 @@ sv:
ResetFilter: Rensa
GridFieldAction_Delete:
DeletePermissionsFailure: 'Rättighet för att radera saknas'
EditPermissionsFailure: 'Rättigheter för avlänkning saknas'
GridFieldDetailForm:
CancelBtn: Avbryt
Create: Skapa
@ -198,6 +214,7 @@ sv:
DeletePermissionsFailure: 'Rättighet för att radera saknas'
Deleted: 'Raderade %s %s'
Save: Spara
Saved: 'Sparade {name} {link}'
GridFieldEditButton_ss:
EDIT: Redigera
GridFieldItemEditView:
@ -209,9 +226,11 @@ sv:
DefaultGroupTitleContentAuthors: 'Författare'
Description: Beskrivning
GroupReminder: 'Om du väljer en förälder till gruppen så kommer gruppen ärva alla förälderns roller'
HierarchyPermsError: 'Den överordnade gruppen "%s" kan inte ges priviligerad tillgång (adminrättigheter krävs)'
Locked: 'Låst?'
NoRoles: 'Inga roller fun'
PLURALNAME: Grupper
Parent: 'Överordnad grupp'
RolesAddEditLink: 'Hantera roller'
SINGULARNAME: Grupp
Sort: 'Sorteringsordning'
@ -250,6 +269,7 @@ sv:
FindInFolder: 'Hitta i mapp'
IMAGEALT: 'Alternativ text (alt)'
IMAGEALTTEXT: 'Alternativ text (alt) - visas om bilden inte kan visas'
IMAGEALTTEXTDESC: 'Visas för skärmläsare eller om bilden inte kan visas'
IMAGEDIMENSIONS: Dimensioner
IMAGEHEIGHTPX: Höjd
IMAGETITLE: 'Titel text (tooltip) - för ytterligare information om bilden'
@ -319,8 +339,10 @@ sv:
EMAIL: E-post
EMPTYNEWPASSWORD: 'Det nya lösenordet kan inte vara tomt, vänligen försök igen'
ENTEREMAIL: 'Ange en e-postadress för att få en återställningslänk för lösenordet.'
ERRORLOCKEDOUT2: 'Ditt konto har tillfälligt stängs av på grund av för många misslyckade inloggningsförsök. Försök igen om {count} minuter.'
ERRORNEWPASSWORD: 'Du har angett ditt nya lösenord annorlunda, försök igen'
ERRORPASSWORDNOTMATCH: 'Ditt nuvarande lösenord matchar inte, var god försök igen'
ERRORWRONGCRED: 'Antingen e-postadressen eller lösenordet är fel. Försök igen.'
FIRSTNAME: 'Förnamn'
INTERFACELANG: 'Gränssnittsspråk'
INVALIDNEWPASSWORD: 'Vi kunde inte godkänna det lösenordet: {password}'
@ -412,6 +434,10 @@ sv:
Pagination:
Page: Sida
View: Visa
PasswordValidator:
LOWCHARSTRENGTH: 'Var god och stärk ditt lösenord genom att lägga till något av följande tecken: %s'
PREVPASSWORD: 'Du har redan använt samma lösenord tidigare, var god och välj ett nytt lösenord'
TOOSHORT: 'Lösenordet är för kort, det måste innehålla %s eller fler tecken.'
Permission:
AdminGroup: Administratör
CMS_ACCESS_CATEGORY: 'CMS-åtkomst'
@ -431,6 +457,7 @@ sv:
Title: Rollnamn
PermissionRoleCode:
PLURALNAME: 'Koder för rollrättigheter'
PermsError: 'Koden "%s" kan inte ges privilegierad tillgång (adminrättigheter krävs)'
SINGULARNAME: 'Kodför rollrättigheter'
Permissions:
PERMISSIONS_CATEGORY: 'Roller och åtkomsträttigheter'
@ -446,6 +473,7 @@ sv:
ERRORPASSWORDPERMISSION: 'Du måste vara inloggad för att kunna ändra ditt lösenord!'
LOGGEDOUT: 'Du har blivit utloggad. Om du vill logga in igen anger du dina uppgifter nedan.'
LOGIN: 'Logga in'
LOSTPASSWORDHEADER: 'Bortglömt lösenord'
NOTEPAGESECURED: 'Den här sidan är låst. Fyll i dina uppgifter nedan så skickar vi dig vidare.'
NOTERESETLINKINVALID: '<p>Återställningslänk för lösenord är felaktig eller för gammal.</p><p>Du kan begära en ny <a href="{link1}">här</a> eller ändra ditt lösenord när du <a href="{link2}">loggat in</a>.</p>'
NOTERESETPASSWORD: 'Ange din e-postadress så skickar vi en länk med vilken du kan återställa ditt lösenord'
@ -474,8 +502,21 @@ sv:
FileFieldLabel: 'CSV-fil <small>(Tillåtna filtyper: *.csv)</small>'
SilverStripeNavigator:
Auto: Auto
ChangeViewMode: 'Ändra visningsläge'
Desktop: Stationär
DualWindowView: 'Delat fönster'
Edit: Ändra
EditView: 'Redigeringsläge'
Mobile: Mobil
PreviewState: 'Förhandsgranskningsstatus'
PreviewView: 'Förhandsgranskningsläge'
Responsive: Responsiv
SplitView: 'Uppdelat läge'
Tablet: Platta
ViewDeviceWidth: 'Välj bredd på förhandsgranskning'
Width: bredd
SiteTree:
TABMAIN: Allmän
TableListField:
CSVEXPORT: 'Exportera till CSV'
Print: Skriv ut

View File

@ -1948,9 +1948,8 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
}
} else {
// Find all the extra fields for all components
$newItems = eval("return (array){$class}::\$many_many_extraFields;");
$newItems = (array)Config::inst()->get($class, 'many_many_extraFields', Config::UNINHERITED);
foreach($newItems as $k => $v) {
if(!is_array($v)) {
@ -1963,9 +1962,11 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
}
}
return isset($items) ? array_merge($newItems, $items) : $newItems;
$items = isset($items) ? array_merge($newItems, $items) : $newItems;
}
}
return isset($items) ? $items : null;
}
/**

View File

@ -647,8 +647,7 @@ class DataQuery {
$model = singleton($modelClass);
if ($component = $model->has_one($rel)) {
if(!$this->query->isJoinedTo($component)) {
$has_one = array_flip($model->has_one());
$foreignKey = $has_one[$component];
$foreignKey = $rel;
$realModelClass = ClassInfo::table_for_object_field($modelClass, "{$foreignKey}ID");
$this->query->addLeftJoin($component,
"\"$component\".\"ID\" = \"{$realModelClass}\".\"{$foreignKey}ID\"");

View File

@ -580,6 +580,7 @@ $gf_grid_x: 16px;
span.non-sortable {
display:block;
padding: 6px 8px;
}
}

View File

@ -10,6 +10,10 @@
clear: both;
}
.description {
margin-left: 0;
}
.middleColumn {
// TODO .middleColumn styling should probably be theme specific (eg cms ui will look different than blackcandy)
// so we should move this style into the cms and black candy files

View File

@ -228,7 +228,7 @@ abstract class SearchFilter extends Object {
* @return DataQuery
*/
protected function applyMany(DataQuery $query) {
throw new InvalidArgumentException(get_class($this) . "can't be used to filter by a list of items.");
throw new InvalidArgumentException(get_class($this) . " can't be used to filter by a list of items.");
}
/**
@ -264,7 +264,7 @@ abstract class SearchFilter extends Object {
* @return DataQuery
*/
protected function excludeMany(DataQuery $query) {
throw new InvalidArgumentException(get_class($this) . "can't be used to filter by a list of items.");
throw new InvalidArgumentException(get_class($this) . " can't be used to filter by a list of items.");
}
/**

View File

@ -120,6 +120,7 @@ class Member extends DataObject implements TemplateGlobalProvider {
'Salt',
'NumVisit'
);
/**
* @config
* @var Array See {@link set_title_columns()}
@ -230,7 +231,7 @@ class Member extends DataObject implements TemplateGlobalProvider {
public static function default_admin() {
// Check if set
if(!Security::has_default_admin()) return null;
// Find or create ADMIN group
singleton('Group')->requireDefaultRecords();
$adminGroup = Permission::get_groups_by_permission('ADMIN')->First();
@ -539,6 +540,8 @@ class Member extends DataObject implements TemplateGlobalProvider {
$hash = $member->encryptWithUserSettings($token);
$member->RememberLoginToken = $hash;
Cookie::set('alc_enc', $member->ID . ':' . $token, 90, null, null, false, true);
$member->NumVisit++;
$member->write();
// Audit logging hook
@ -1442,8 +1445,8 @@ class Member extends DataObject implements TemplateGlobalProvider {
if(!($member && $member->exists())) return false;
// If the requesting member is not an admin, but has access to manage members,
// he still can't edit other members with ADMIN permission.
// This is a bit weak, strictly speaking he shouldn't be allowed to
// they still can't edit other members with ADMIN permission.
// This is a bit weak, strictly speaking they shouldn't be allowed to
// perform any action that could change the password on a member
// with "higher" permissions than himself, but thats hard to determine.
if(!Permission::checkMember($member, 'ADMIN') && Permission::checkMember($this, 'ADMIN')) return false;

View File

@ -227,7 +227,7 @@ JS;
return $this->controller->redirect(Director::absoluteBaseURL() . Security::config()->default_login_dest);
}
// Redirect the user to the page where he came from
// Redirect the user to the page where they came from
$member = Member::currentUser();
if($member) {
$firstname = Convert::raw2xml($member->FirstName);

View File

@ -0,0 +1,107 @@
<?php
/**
* @package framework
* @subpackage tests
*/
class TreeDropdownFieldTest extends SapphireTest {
protected static $fixture_file = 'TreeDropdownFieldTest.yml';
public function testTreeSearch(){
$field = new TreeDropdownField('TestTree', 'Test tree', 'Folder');
// case insensitive search against keyword 'sub' for folders
$request = new SS_HTTPRequest('GET','url',array('search'=>'sub'));
$tree = $field->tree($request);
$folder1 = $this->objFromFixture('Folder','folder1');
$folder1Subfolder1 = $this->objFromFixture('Folder','folder1-subfolder1');
$parser = new CSSContentParser($tree);
$cssPath = 'ul.tree li#selector-TestTree-'.$folder1->ID.' li#selector-TestTree-'.$folder1Subfolder1->ID.' a span.item';
$firstResult = $parser->getBySelector($cssPath);
$this->assertEquals(
(string)$firstResult[0],
$folder1Subfolder1->Name,
$folder1Subfolder1->Name.' is found, nested under '.$folder1->Name
);
$subfolder = $this->objFromFixture('Folder','subfolder');
$cssPath = 'ul.tree li#selector-TestTree-'.$subfolder->ID.' a span.item';
$secondResult = $parser->getBySelector($cssPath);
$this->assertEquals(
(string)$secondResult[0],
$subfolder->Name,
$subfolder->Name.' is found at root level'
);
// other folders which don't contain the keyword 'sub' are not returned in search results
$folder2 = $this->objFromFixture('Folder','folder2');
$cssPath = 'ul.tree li#selector-TestTree-'.$folder2->ID.' a span.item';
$noResult = $parser->getBySelector($cssPath);
$this->assertEquals(
$noResult,
array(),
$folder2.' is not found'
);
$field = new TreeDropdownField('TestTree', 'Test tree', 'File');
// case insensitive search against keyword 'sub' for files
$request = new SS_HTTPRequest('GET','url',array('search'=>'sub'));
$tree = $field->tree($request);
$parser = new CSSContentParser($tree);
// Even if we used File as the source object, folders are still returned because Folder is a File
$cssPath = 'ul.tree li#selector-TestTree-'.$folder1->ID.' li#selector-TestTree-'.$folder1Subfolder1->ID.' a span.item';
$firstResult = $parser->getBySelector($cssPath);
$this->assertEquals(
(string)$firstResult[0],
$folder1Subfolder1->Name,
$folder1Subfolder1->Name.' is found, nested under '.$folder1->Name
);
// Looking for two files with 'sub' in their name, both under the same folder
$file1 = $this->objFromFixture('File','subfolderfile1');
$file2 = $this->objFromFixture('File','subfolderfile2');
$cssPath = 'ul.tree li#selector-TestTree-'.$subfolder->ID.' li#selector-TestTree-'.$file1->ID.' a';
$firstResult = $parser->getBySelector($cssPath);
$this->assertGreaterThan(
0,
count($firstResult),
$file1->Name.' with ID '.$file1->ID.' is in search results'
);
$this->assertEquals(
(string)$firstResult[0],
$file1->Name,
$file1->Name.' is found nested under '.$subfolder->Name
);
$cssPath = 'ul.tree li#selector-TestTree-'.$subfolder->ID.' li#selector-TestTree-'.$file2->ID.' a';
$secondResult = $parser->getBySelector($cssPath);
$this->assertGreaterThan(
0,
count($secondResult),
$file2->Name.' with ID '.$file2->ID.' is in search results'
);
$this->assertEquals(
(string)$secondResult[0],
$file2->Name,
$file2->Name.' is found nested under '.$subfolder->Name
);
// other files which don't include 'sub' are not returned in search results
$file3 = $this->objFromFixture('File','asdf');
$cssPath = 'ul.tree li#selector-TestTree-'.$file3->ID;
$noResult = $parser->getBySelector($cssPath);
$this->assertEquals(
$noResult,
array(),
$file3->Name.' is not found'
);
}
}

View File

@ -0,0 +1,25 @@
Folder:
subfolder:
Name: FileTest-subfolder
folder1:
Name: FileTest-folder1
folder2:
Name: FileTest-folder2
folder1-subfolder1:
Name: FileTest-folder1-subfolder1
ParentID: =>Folder.folder1
File:
asdf:
Filename: assets/FileTest.txt
subfolderfile1:
Filename: assets/FileTest-subfolder/TestFile1InSubfolder.txt
Name: TestFile1InSubfolder
ParentID: =>Folder.subfolder
subfolderfile2:
Filename: assets/FileTest-subfolder/TestFile2InSubfolder.txt
Name: TestFile2InSubfolder
ParentID: =>Folder.subfolder
file1-folder1:
Filename: assets/FileTest-folder1/File1.txt
Name: File1.txt
ParentID: =>Folder.folder1

View File

@ -32,7 +32,7 @@ class GridFieldEditButtonTest extends SapphireTest {
// Check that there are content
$this->assertEquals(3, count($content->getBySelector('.ss-gridfield-item')));
// Make sure that there are edit links, even though the user doesn't have "edit" permissions
// (he can still view the records)
// (they can still view the records)
$this->assertEquals(2, count($content->getBySelector('.edit-link')),
'Edit links should show when not logged in.');
}

View File

@ -417,6 +417,9 @@ PHP;
$entities = array(
'Level1.Level2.EntityName' => array('Text', 'Context'),
'Level1.OtherEntityName' => array('Other Text', 'Other Context'),
'Level1.BoolTest' => array('True'),
'Level1.FlagTest' => array('No'),
'Level1.TextTest' => array('Maybe')
);
$yaml = <<<YAML
de:
@ -424,6 +427,9 @@ de:
Level2:
EntityName: Text
OtherEntityName: 'Other Text'
BoolTest: 'True'
FlagTest: 'No'
TextTest: Maybe
YAML;
$this->assertEquals($yaml, Convert::nl2os($writer->getYaml($entities, 'de')));

View File

@ -1082,6 +1082,20 @@ class DataObjectTest extends SapphireTest {
$player = $this->objFromFixture('DataObjectTest_Player', 'player1');
$team = $this->objFromFixture('DataObjectTest_Team', 'team1');
// Get all extra fields
$teamExtraFields = $team->many_many_extraFields();
$this->assertEquals(array(
'Players' => array('Position' => 'Varchar(100)')
), $teamExtraFields);
// Ensure fields from parent classes are included
$subTeam = singleton('DataObjectTest_SubTeam');
$teamExtraFields = $subTeam->many_many_extraFields();
$this->assertEquals(array(
'Players' => array('Position' => 'Varchar(100)'),
'FormerPlayers' => array('Position' => 'Varchar(100)')
), $teamExtraFields);
// Extra fields are immediately available on the Team class (defined in $many_many_extraFields)
$teamExtraFields = $team->many_many_extraFields('Players');
$this->assertEquals($teamExtraFields, array(
@ -1610,6 +1624,16 @@ class DataObjectTest_SubTeam extends DataObjectTest_Team implements TestOnly {
private static $has_one = array(
"ParentTeam" => 'DataObjectTest_Team',
);
private static $many_many = array(
'FormerPlayers' => 'DataObjectTest_Player'
);
private static $many_many_extraFields = array(
'FormerPlayers' => array(
'Position' => 'Varchar(100)'
)
);
}
class OtherSubclassWithSameField extends DataObjectTest_Team implements TestOnly {
private static $db = array(

View File

@ -49,6 +49,19 @@ class DataQueryTest extends SapphireTest {
$dq->sql($parameters));
}
public function testApplyRelation() {
// Test applyRelation with two has_ones pointing to the same class
$dq = new DataQuery('DataQueryTest_B');
$dq->applyRelation('TestC');
$this->assertTrue($dq->query()->isJoinedTo('DataQueryTest_C'));
$this->assertContains('"DataQueryTest_C"."ID" = "DataQueryTest_B"."TestCID"', $dq->sql());
$dq = new DataQuery('DataQueryTest_B');
$dq->applyRelation('TestCTwo');
$this->assertTrue($dq->query()->isJoinedTo('DataQueryTest_C'));
$this->assertContains('"DataQueryTest_C"."ID" = "DataQueryTest_B"."TestCTwoID"', $dq->sql());
}
public function testApplyReplationDeepInheretence() {
$newDQ = new DataQuery('DataQueryTest_E');
//apply a relation to a relation from an ancestor class
@ -257,6 +270,7 @@ class DataQueryTest_B extends DataObject implements TestOnly {
private static $has_one = array(
'TestC' => 'DataQueryTest_C',
'TestCTwo' => 'DataQueryTest_C',
);
}
@ -273,7 +287,8 @@ class DataQueryTest_C extends DataObject implements TestOnly {
private static $has_many = array(
'TestAs' => 'DataQueryTest_A',
'TestBs' => 'DataQueryTest_B',
'TestBs' => 'DataQueryTest_B.TestC',
'TestBsTwo' => 'DataQueryTest_B.TestCTwo',
);
private static $many_many = array(

View File

@ -10,7 +10,7 @@ jQuery(function($){
nextText: 'Neste&raquo;',
currentText: 'I dag',
monthNames: ['januar','februar','mars','april','mai','juni','juli','august','september','oktober','november','desember'],
monthNamesShort: ['jan.','feb.','mar.','apr.','mai','juni','juli','aug.','sep.','okt.','nov.','des.'],
monthNamesShort: ['jan.','feb.','mars','apr.','mai','juni','juli','aug.','sep.','okt.','nov.','des.'],
dayNames: ['søndag','mandag','tirsdag','onsdag','torsdag','fredag','lørdag'],
dayNamesShort: ['søn','man','tir','ons','tor','fre','lør'],
dayNamesMin: ['Sø','Ma','Ti','On','To','Fr','Lø'],