/** * Returns a readonly span containing the correct value. * * @param array $properties * * @return string */ public function Field($properties = array()) { $source = ArrayLib::flatten($this->getSource()); $values = $this->getValueArray(); // Get selected values $mapped = array(); foreach ($values as $value) { if (isset($source[$value])) { $mapped[] = $source[$value]; } } // Don't check if string arguments are matching against the source, // as they might be generated HTML diff views instead of the actual values if ($this->value && is_string($this->value) && empty($mapped)) { $mapped = array(trim($this->value)); $values = array(); } if ($mapped) { $attrValue = implode(', ', array_values($mapped)); $attrValue = Convert::raw2xml($attrValue); $inputValue = implode(', ', array_values($values)); } else { $attrValue = '<i>(' . _t('FormField.NONE', 'none') . ')</i>'; $inputValue = ''; } $properties = array_merge($properties, array('AttrValue' => DBField::create_field('HTMLFragment', $attrValue), 'InputValue' => $inputValue)); return parent::Field($properties); }
public function testFlatten() { $options = array('1' => 'one', '2' => 'two'); $expected = $options; $this->assertEquals($expected, ArrayLib::flatten($options)); $options = array('1' => array('2' => 'two', '3' => 'three'), '4' => 'four'); $expected = array('2' => 'two', '3' => 'three', '4' => 'four'); $this->assertEquals($expected, ArrayLib::flatten($options)); }
public function testRefusesToWrapAnIndexedArray() { $array = array(0 => "One", 1 => "Two"); $this->assertFalse(ArrayLib::is_associative($array)); /* * Expect user_error() to be called below, if enabled * (tobych) That should be an exception. Something like: * $this->setExpectedException('InvalidArgumentException'); */ // $arrayData = new ArrayData($array); }
/** * Gets a field from this object. * * * If the value is an object but not an instance of * ViewableData, it will be converted recursively to an * ArrayData. * * If the value is an associative array, it will likewise be * converted recursively to an ArrayData. * * @param string $field * @return mixed */ public function getField($field) { $value = $this->array[$field]; if (is_object($value) && !$value instanceof ViewableData) { return new ArrayData($value); } elseif (ArrayLib::is_associative($value)) { return new ArrayData($value); } else { return $value; } }
/** * Builds a new currency field based on the allowed currencies configured * * @return FormField */ protected function buildCurrencyField() { $name = $this->getName(); $allowedCurrencies = $this->getAllowedCurrencies(); if ($allowedCurrencies) { $field = new DropdownField("{$name}[Currency]", _t('MoneyField.FIELDLABELCURRENCY', 'Currency'), ArrayLib::is_associative($allowedCurrencies) ? $allowedCurrencies : array_combine($allowedCurrencies, $allowedCurrencies)); } else { $field = new TextField("{$name}[Currency]", _t('MoneyField.FIELDLABELCURRENCY', 'Currency')); } $field->setReadonly($this->isReadonly()); $field->setDisabled($this->isDisabled()); return $field; }
/** * @covers SilverStripe\Core\ClassInfo::ancestry() */ public function testAncestry() { $ancestry = ClassInfo::ancestry('ClassInfoTest_ChildClass'); $expect = ArrayLib::valuekey(array('SilverStripe\\Core\\Object', 'SilverStripe\\View\\ViewableData', 'SilverStripe\\ORM\\DataObject', 'ClassInfoTest_BaseClass', 'ClassInfoTest_ChildClass')); $this->assertEquals($expect, $ancestry); ClassInfo::reset_db_cache(); $this->assertEquals($expect, ClassInfo::ancestry('classINFOTest_Childclass')); ClassInfo::reset_db_cache(); $this->assertEquals($expect, ClassInfo::ancestry('classINFOTest_Childclass')); ClassInfo::reset_db_cache(); $ancestry = ClassInfo::ancestry('ClassInfoTest_ChildClass', true); $this->assertEquals(array('ClassInfoTest_BaseClass' => 'ClassInfoTest_BaseClass'), $ancestry, '$tablesOnly option excludes memory-only inheritance classes'); }
public function testEnumParsing() { $enum = new DBEnum('testField', "\n\t\t\t,\n\t\t\t0,\n\t\t\tItem1,\n\t\t\tItem2,\n\t\t\tItem 3,\n\t\t\tItem-4,\n\t\t\titem 5\n\t\t\tstill 5,\n\t\t\ttrailing comma,\n\t\t"); $this->assertEquals(ArrayLib::valuekey(array('', '0', 'Item1', 'Item2', 'Item 3', 'Item-4', 'item 5 still 5', 'trailing comma')), $enum->enumValues()); }
/** * Returns all combined HTTP GET and POST parameters * passed into this request. If a parameter with the same * name exists in both arrays, the POST value is returned. * * @return array */ public function requestVars() { return ArrayLib::array_merge_recursive($this->getVars, $this->postVars); }
/** * Merge some arbitrary data in with this object. This method returns a {@link ViewableData_Customised} instance * with references to both this and the new custom data. * * Note that any fields you specify will take precedence over the fields on this object. * * @param array|ViewableData $data * @return ViewableData_Customised */ public function customise($data) { if (is_array($data) && (empty($data) || ArrayLib::is_associative($data))) { $data = new ArrayData($data); } if ($data instanceof ViewableData) { return new ViewableData_Customised($this, $data); } throw new InvalidArgumentException('ViewableData->customise(): $data must be an associative array or a ViewableData instance'); }
public function getList() { $context = $this->getSearchContext(); $params = $this->getRequest()->requestVar('q'); if (is_array($params)) { $params = ArrayLib::array_map_recursive('trim', $params); // Parse all DateFields to handle user input non ISO 8601 dates foreach ($context->getFields() as $field) { if ($field instanceof DatetimeField) { $params[$field->getName()] = date('Y-m-d', strtotime($params[$field->getName()])); } } } $list = $context->getResults($params); $this->extend('updateList', $list); return $list; }
/** * Test a URL request, returning a response object. This method is the counterpart of * Director::direct() that is used in functional testing. It will execute the URL given, and * return the result as an HTTPResponse object. * * @uses Controller::handleRequest() Handles the page logic for a Director::direct() call. * * @param string $url The URL to visit. * @param array $postVars The $_POST & $_FILES variables. * @param array|Session $session The {@link Session} object representing the current session. * By passing the same object to multiple calls of Director::test(), you can simulate a persisted * session. * @param string $httpMethod The HTTP method, such as GET or POST. It will default to POST if * postVars is set, GET otherwise. Overwritten by $postVars['_method'] if present. * @param string $body The HTTP body. * @param array $headers HTTP headers with key-value pairs. * @param array|Cookie_Backend $cookies to populate $_COOKIE. * @param HTTPRequest $request The {@see SS_HTTP_Request} object generated as a part of this request. * * @return HTTPResponse * * @throws HTTPResponse_Exception */ public static function test($url, $postVars = null, $session = array(), $httpMethod = null, $body = null, $headers = array(), $cookies = array(), &$request = null) { Config::nest(); Injector::nest(); // These are needed so that calling Director::test() does not muck with whoever is calling it. // Really, it's some inappropriate coupling and should be resolved by making less use of statics. $oldReadingMode = Versioned::get_reading_mode(); $getVars = array(); if (!$httpMethod) { $httpMethod = $postVars || is_array($postVars) ? "POST" : "GET"; } if (!$session) { $session = Injector::inst()->create('SilverStripe\\Control\\Session', array()); } $cookieJar = $cookies instanceof Cookie_Backend ? $cookies : Injector::inst()->createWithArgs('SilverStripe\\Control\\Cookie_Backend', array($cookies ?: array())); // Back up the current values of the superglobals $existingRequestVars = isset($_REQUEST) ? $_REQUEST : array(); $existingGetVars = isset($_GET) ? $_GET : array(); $existingPostVars = isset($_POST) ? $_POST : array(); $existingSessionVars = isset($_SESSION) ? $_SESSION : array(); $existingCookies = isset($_COOKIE) ? $_COOKIE : array(); $existingServer = isset($_SERVER) ? $_SERVER : array(); $existingRequirementsBackend = Requirements::backend(); Cookie::config()->update('report_errors', false); Requirements::set_backend(Requirements_Backend::create()); // Set callback to invoke prior to return $onCleanup = function () use($existingRequestVars, $existingGetVars, $existingPostVars, $existingSessionVars, $existingCookies, $existingServer, $existingRequirementsBackend, $oldReadingMode) { // Restore the super globals $_REQUEST = $existingRequestVars; $_GET = $existingGetVars; $_POST = $existingPostVars; $_SESSION = $existingSessionVars; $_COOKIE = $existingCookies; $_SERVER = $existingServer; Requirements::set_backend($existingRequirementsBackend); // These are needed so that calling Director::test() does not muck with whoever is calling it. // Really, it's some inappropriate coupling and should be resolved by making less use of statics Versioned::set_reading_mode($oldReadingMode); Injector::unnest(); // Restore old CookieJar, etc Config::unnest(); }; if (strpos($url, '#') !== false) { $url = substr($url, 0, strpos($url, '#')); } // Handle absolute URLs if (parse_url($url, PHP_URL_HOST)) { $bits = parse_url($url); // If a port is mentioned in the absolute URL, be sure to add that into the HTTP host if (isset($bits['port'])) { $_SERVER['HTTP_HOST'] = $bits['host'] . ':' . $bits['port']; } else { $_SERVER['HTTP_HOST'] = $bits['host']; } } // Ensure URL is properly made relative. // Example: url passed is "/ss31/my-page" (prefixed with BASE_URL), this should be changed to "my-page" $url = self::makeRelative($url); $urlWithQuerystring = $url; if (strpos($url, '?') !== false) { list($url, $getVarsEncoded) = explode('?', $url, 2); parse_str($getVarsEncoded, $getVars); } // Replace the super globals with appropriate test values $_REQUEST = ArrayLib::array_merge_recursive((array) $getVars, (array) $postVars); $_GET = (array) $getVars; $_POST = (array) $postVars; $_SESSION = $session ? $session->inst_getAll() : array(); $_COOKIE = $cookieJar->getAll(false); Injector::inst()->registerService($cookieJar, 'SilverStripe\\Control\\Cookie_Backend'); $_SERVER['REQUEST_URI'] = Director::baseURL() . $urlWithQuerystring; $request = new HTTPRequest($httpMethod, $url, $getVars, $postVars, $body); if ($headers) { foreach ($headers as $k => $v) { $request->addHeader($k, $v); } } // Pre-request filtering // @see issue #2517 $model = DataModel::inst(); $output = Injector::inst()->get('SilverStripe\\Control\\RequestProcessor')->preRequest($request, $session, $model); if ($output === false) { $onCleanup(); throw new HTTPResponse_Exception(_t('Director.INVALID_REQUEST', 'Invalid request'), 400); } // TODO: Pass in the DataModel $result = Director::handleRequest($request, $session, $model); // Ensure that the result is an HTTPResponse object if (is_string($result)) { if (substr($result, 0, 9) == 'redirect:') { $response = new HTTPResponse(); $response->redirect(substr($result, 9)); $result = $response; } else { $result = new HTTPResponse($result); } } $output = Injector::inst()->get('SilverStripe\\Control\\RequestProcessor')->postRequest($request, $result, $model); if ($output === false) { $onCleanup(); throw new HTTPResponse_Exception("Invalid response"); } // Return valid response $onCleanup(); return $result; }
/** * Returns a list of classes that inherit from the given class. * The resulting array includes the base class passed * through the $class parameter as the first array value. * * Example usage: * <code> * ClassInfo::subclassesFor('BaseClass'); * array( * 'BaseClass' => 'BaseClass', * 'ChildClass' => 'ChildClass', * 'GrandChildClass' => 'GrandChildClass' * ) * </code> * * @param string|object $nameOrObject The classname or object * @return array Names of all subclasses as an associative array. */ public static function subclassesFor($nameOrObject) { if (is_string($nameOrObject) && !class_exists($nameOrObject)) { return []; } //normalise class case $className = self::class_name($nameOrObject); $descendants = ClassLoader::instance()->getManifest()->getDescendantsOf($className); $result = array($className => $className); if ($descendants) { return $result + ArrayLib::valuekey($descendants); } else { return $result; } }
/** * This is the main translator function. Returns the string defined by $class and $entity according to the * currently set locale. * * @param string $entity Entity that identifies the string. It must be in the form "Namespace.Entity" where * Namespace will be usually the class name where this string is used and Entity identifies * the string inside the namespace. * @param string $string The original string itself. In a usual call this is a mandatory parameter, but if you are * reusing a string which has already been "declared" (using another call to this function, * with the same class and entity), you can omit it. * @param string $context (optional) If the string can be difficult to translate by any reason, you can help * translators with some more info using this param * @param array $injection (optional) array of key value pairs that are used to replace corresponding * expressions in {curly brackets} in the $string. The injection array can also be * used as the their argument to the _t() function * @return string The translated string, according to the currently set locale {@link i18n::set_locale()} */ public static function _t($entity, $string = "", $context = "", $injection = null) { //fetch the injection array out of the parameters (if it is present) $argList = func_get_args(); $argNum = func_num_args(); //_t($entity, $string = "", $context (optional), $injectionArray (optional)) $injectionArray = null; for ($i = 0; $i < $argNum; $i++) { if (is_array($argList[$i])) { //we have reached the injectionArray $injectionArray = $argList[$i]; //any array in the args will be the injection array } } // Find best translation $locale = i18n::get_locale(); $returnValue = static::with_translators(function (Zend_Translate_Adapter $adapter) use($entity, $locale) { // Return translation only if we found a match thats not the entity itself (Zend fallback) $translation = $adapter->translate($entity, $locale); if ($translation && $translation != $entity) { return $translation; } return null; }); // Fall back to default string argument if ($returnValue === null) { $returnValue = is_string($string) ? $string : ''; } // inject the variables from injectionArray (if present) if ($injectionArray) { $regex = '/\\{[\\w\\d]*\\}/i'; if (!preg_match($regex, $returnValue)) { // Legacy mode: If no injection placeholders are found, // replace sprintf placeholders in fixed order. // Fail silently in case the translation is outdated preg_match_all('/%[s,d]/', $returnValue, $returnValueArgs); if ($returnValueArgs) { foreach ($returnValueArgs[0] as $i => $returnValueArg) { if ($i >= count($injectionArray)) { $injectionArray[] = ''; } } } $replaced = vsprintf($returnValue, array_values($injectionArray)); if ($replaced) { $returnValue = $replaced; } } else { if (!ArrayLib::is_associative($injectionArray)) { // Legacy mode: If injection placeholders are found, // but parameters are passed without names, replace them in fixed order. $returnValue = preg_replace_callback($regex, function () use(&$injectionArray) { return $injectionArray ? array_shift($injectionArray) : ''; }, $returnValue); } else { // Standard placeholder replacement with named injections and variable order. foreach ($injectionArray as $variable => $injection) { $placeholder = '{' . $variable . '}'; $returnValue = str_replace($placeholder, $injection, $returnValue, $count); if (!$count) { Injector::inst()->get('Logger')->log('notice', sprintf("Couldn't find placeholder '%s' in translation string '%s' (id: '%s')", $placeholder, $returnValue, $entity)); } } } } } return $returnValue; }
/** * Add {@link RequiredField} objects together * * @param RequiredFields $requiredFields * @return RequiredFields */ public function appendRequiredFields($requiredFields) { $this->required = $this->required + ArrayLib::valuekey($requiredFields->getRequired()); return $this; }