diff --git a/.gitignore b/.gitignore index e2ff07d14d..ae63693170 100644 --- a/.gitignore +++ b/.gitignore @@ -34,6 +34,9 @@ RCS/* # netbeans nbproject +# phpStorm +.idea + # geany *.geany diff --git a/3rdparty/Crypt_Blowfish/Blowfish.php b/3rdparty/Crypt_Blowfish/Blowfish.php index a7b8948f04..4ccacb963e 100644 --- a/3rdparty/Crypt_Blowfish/Blowfish.php +++ b/3rdparty/Crypt_Blowfish/Blowfish.php @@ -221,7 +221,7 @@ class Crypt_Blowfish function decrypt($cipherText) { if (!is_string($cipherText)) { - PEAR::raiseError('Chiper text must be a string', 1, PEAR_ERROR_DIE); + PEAR::raiseError('Cipher text must be a string', 1, PEAR_ERROR_DIE); } if (extension_loaded('mcrypt')) { diff --git a/3rdparty/Dropbox/API.php b/3rdparty/Dropbox/API.php new file mode 100644 index 0000000000..8cdce678e1 --- /dev/null +++ b/3rdparty/Dropbox/API.php @@ -0,0 +1,380 @@ +oauth = $oauth; + $this->root = $root; + $this->useSSL = $useSSL; + if (!$this->useSSL) + { + throw new Dropbox_Exception('Dropbox REST API now requires that all requests use SSL'); + } + + } + + /** + * Returns information about the current dropbox account + * + * @return stdclass + */ + public function getAccountInfo() { + + $data = $this->oauth->fetch($this->api_url . 'account/info'); + return json_decode($data['body'],true); + + } + + /** + * Returns a file's contents + * + * @param string $path path + * @param string $root Use this to override the default root path (sandbox/dropbox) + * @return string + */ + public function getFile($path = '', $root = null) { + + if (is_null($root)) $root = $this->root; + $path = str_replace(array('%2F','~'), array('/','%7E'), rawurlencode($path)); + $result = $this->oauth->fetch($this->api_content_url . 'files/' . $root . '/' . ltrim($path,'/')); + return $result['body']; + + } + + /** + * Uploads a new file + * + * @param string $path Target path (including filename) + * @param string $file Either a path to a file or a stream resource + * @param string $root Use this to override the default root path (sandbox/dropbox) + * @return bool + */ + public function putFile($path, $file, $root = null) { + + $directory = dirname($path); + $filename = basename($path); + + if($directory==='.') $directory = ''; + $directory = str_replace(array('%2F','~'), array('/','%7E'), rawurlencode($directory)); +// $filename = str_replace('~', '%7E', rawurlencode($filename)); + if (is_null($root)) $root = $this->root; + + if (is_string($file)) { + + $file = fopen($file,'rb'); + + } elseif (!is_resource($file)) { + throw new Dropbox_Exception('File must be a file-resource or a string'); + } + $result=$this->multipartFetch($this->api_content_url . 'files/' . + $root . '/' . trim($directory,'/'), $file, $filename); + + if(!isset($result["httpStatus"]) || $result["httpStatus"] != 200) + throw new Dropbox_Exception("Uploading file to Dropbox failed"); + + return true; + } + + + /** + * Copies a file or directory from one location to another + * + * This method returns the file information of the newly created file. + * + * @param string $from source path + * @param string $to destination path + * @param string $root Use this to override the default root path (sandbox/dropbox) + * @return stdclass + */ + public function copy($from, $to, $root = null) { + + if (is_null($root)) $root = $this->root; + $response = $this->oauth->fetch($this->api_url . 'fileops/copy', array('from_path' => $from, 'to_path' => $to, 'root' => $root), 'POST'); + + return json_decode($response['body'],true); + + } + + /** + * Creates a new folder + * + * This method returns the information from the newly created directory + * + * @param string $path + * @param string $root Use this to override the default root path (sandbox/dropbox) + * @return stdclass + */ + public function createFolder($path, $root = null) { + + if (is_null($root)) $root = $this->root; + + // Making sure the path starts with a / +// $path = '/' . ltrim($path,'/'); + + $response = $this->oauth->fetch($this->api_url . 'fileops/create_folder', array('path' => $path, 'root' => $root),'POST'); + return json_decode($response['body'],true); + + } + + /** + * Deletes a file or folder. + * + * This method will return the metadata information from the deleted file or folder, if successful. + * + * @param string $path Path to new folder + * @param string $root Use this to override the default root path (sandbox/dropbox) + * @return array + */ + public function delete($path, $root = null) { + + if (is_null($root)) $root = $this->root; + $response = $this->oauth->fetch($this->api_url . 'fileops/delete', array('path' => $path, 'root' => $root), 'POST'); + return json_decode($response['body']); + + } + + /** + * Moves a file or directory to a new location + * + * This method returns the information from the newly created directory + * + * @param mixed $from Source path + * @param mixed $to destination path + * @param string $root Use this to override the default root path (sandbox/dropbox) + * @return stdclass + */ + public function move($from, $to, $root = null) { + + if (is_null($root)) $root = $this->root; + $response = $this->oauth->fetch($this->api_url . 'fileops/move', array('from_path' => rawurldecode($from), 'to_path' => rawurldecode($to), 'root' => $root), 'POST'); + + return json_decode($response['body'],true); + + } + + /** + * Returns file and directory information + * + * @param string $path Path to receive information from + * @param bool $list When set to true, this method returns information from all files in a directory. When set to false it will only return infromation from the specified directory. + * @param string $hash If a hash is supplied, this method simply returns true if nothing has changed since the last request. Good for caching. + * @param int $fileLimit Maximum number of file-information to receive + * @param string $root Use this to override the default root path (sandbox/dropbox) + * @return array|true + */ + public function getMetaData($path, $list = true, $hash = null, $fileLimit = null, $root = null) { + + if (is_null($root)) $root = $this->root; + + $args = array( + 'list' => $list, + ); + + if (!is_null($hash)) $args['hash'] = $hash; + if (!is_null($fileLimit)) $args['file_limit'] = $fileLimit; + + $path = str_replace(array('%2F','~'), array('/','%7E'), rawurlencode($path)); + $response = $this->oauth->fetch($this->api_url . 'metadata/' . $root . '/' . ltrim($path,'/'), $args); + + /* 304 is not modified */ + if ($response['httpStatus']==304) { + return true; + } else { + return json_decode($response['body'],true); + } + + } + + /** + * A way of letting you keep up with changes to files and folders in a user's Dropbox. You can periodically call /delta to get a list of "delta entries", which are instructions on how to update your local state to match the server's state. + * + * This method returns the information from the newly created directory + * + * @param string $cursor A string that is used to keep track of your current state. On the next call pass in this value to return delta entries that have been recorded since the cursor was returned. + * @return stdclass + */ + public function delta($cursor) { + + $arg['cursor'] = $cursor; + + $response = $this->oauth->fetch($this->api_url . 'delta', $arg, 'POST'); + return json_decode($response['body'],true); + + } + + /** + * Returns a thumbnail (as a string) for a file path. + * + * @param string $path Path to file + * @param string $size small, medium or large + * @param string $root Use this to override the default root path (sandbox/dropbox) + * @return string + */ + public function getThumbnail($path, $size = 'small', $root = null) { + + if (is_null($root)) $root = $this->root; + $path = str_replace(array('%2F','~'), array('/','%7E'), rawurlencode($path)); + $response = $this->oauth->fetch($this->api_content_url . 'thumbnails/' . $root . '/' . ltrim($path,'/'),array('size' => $size)); + + return $response['body']; + + } + + /** + * This method is used to generate multipart POST requests for file upload + * + * @param string $uri + * @param array $arguments + * @return bool + */ + protected function multipartFetch($uri, $file, $filename) { + + /* random string */ + $boundary = 'R50hrfBj5JYyfR3vF3wR96GPCC9Fd2q2pVMERvEaOE3D8LZTgLLbRpNwXek3'; + + $headers = array( + 'Content-Type' => 'multipart/form-data; boundary=' . $boundary, + ); + + $body="--" . $boundary . "\r\n"; + $body.="Content-Disposition: form-data; name=file; filename=".rawurldecode($filename)."\r\n"; + $body.="Content-type: application/octet-stream\r\n"; + $body.="\r\n"; + $body.=stream_get_contents($file); + $body.="\r\n"; + $body.="--" . $boundary . "--"; + + // Dropbox requires the filename to also be part of the regular arguments, so it becomes + // part of the signature. + $uri.='?file=' . $filename; + + return $this->oauth->fetch($uri, $body, 'POST', $headers); + + } + + + /** + * Search + * + * Returns metadata for all files and folders that match the search query. + * + * @added by: diszo.sasil + * + * @param string $query + * @param string $root Use this to override the default root path (sandbox/dropbox) + * @param string $path + * @return array + */ + public function search($query = '', $root = null, $path = ''){ + if (is_null($root)) $root = $this->root; + if(!empty($path)){ + $path = str_replace(array('%2F','~'), array('/','%7E'), rawurlencode($path)); + } + $response = $this->oauth->fetch($this->api_url . 'search/' . $root . '/' . ltrim($path,'/'),array('query' => $query)); + return json_decode($response['body'],true); + } + + /** + * Creates and returns a shareable link to files or folders. + * + * Note: Links created by the /shares API call expire after thirty days. + * + * @param type $path + * @param type $root + * @return type + */ + public function share($path, $root = null) { + if (is_null($root)) $root = $this->root; + $path = str_replace(array('%2F','~'), array('/','%7E'), rawurlencode($path)); + $response = $this->oauth->fetch($this->api_url. 'shares/'. $root . '/' . ltrim($path, '/'), array(), 'POST'); + return json_decode($response['body'],true); + + } + + /** + * Returns a link directly to a file. + * Similar to /shares. The difference is that this bypasses the Dropbox webserver, used to provide a preview of the file, so that you can effectively stream the contents of your media. + * + * Note: The /media link expires after four hours, allotting enough time to stream files, but not enough to leave a connection open indefinitely. + * + * @param type $path + * @param type $root + * @return type + */ + public function media($path, $root = null) { + + if (is_null($root)) $root = $this->root; + $path = str_replace(array('%2F','~'), array('/','%7E'), rawurlencode($path)); + $response = $this->oauth->fetch($this->api_url. 'media/'. $root . '/' . ltrim($path, '/'), array(), 'POST'); + return json_decode($response['body'],true); + + } + + /** + * Creates and returns a copy_ref to a file. This reference string can be used to copy that file to another user's Dropbox by passing it in as the from_copy_ref parameter on /fileops/copy. + * + * @param type $path + * @param type $root + * @return type + */ + public function copy_ref($path, $root = null) { + + if (is_null($root)) $root = $this->root; + $path = str_replace(array('%2F','~'), array('/','%7E'), rawurlencode($path)); + $response = $this->oauth->fetch($this->api_url. 'copy_ref/'. $root . '/' . ltrim($path, '/')); + return json_decode($response['body'],true); + + } + + +} diff --git a/3rdparty/Dropbox/Exception.php b/3rdparty/Dropbox/Exception.php new file mode 100644 index 0000000000..50cbc4c791 --- /dev/null +++ b/3rdparty/Dropbox/Exception.php @@ -0,0 +1,15 @@ +oauth_token = $token['token']; + $this->oauth_token_secret = $token['token_secret']; + } else { + $this->oauth_token = $token; + $this->oauth_token_secret = $token_secret; + } + + } + + /** + * Returns the oauth request tokens as an associative array. + * + * The array will contain the elements 'token' and 'token_secret'. + * + * @return array + */ + public function getToken() { + + return array( + 'token' => $this->oauth_token, + 'token_secret' => $this->oauth_token_secret, + ); + + } + + /** + * Returns the authorization url + * + * @param string $callBack Specify a callback url to automatically redirect the user back + * @return string + */ + public function getAuthorizeUrl($callBack = null) { + + // Building the redirect uri + $token = $this->getToken(); + $uri = self::URI_AUTHORIZE . '?oauth_token=' . $token['token']; + if ($callBack) $uri.='&oauth_callback=' . $callBack; + return $uri; + } + + /** + * Fetches a secured oauth url and returns the response body. + * + * @param string $uri + * @param mixed $arguments + * @param string $method + * @param array $httpHeaders + * @return string + */ + public abstract function fetch($uri, $arguments = array(), $method = 'GET', $httpHeaders = array()); + + /** + * Requests the OAuth request token. + * + * @return array + */ + abstract public function getRequestToken(); + + /** + * Requests the OAuth access tokens. + * + * @return array + */ + abstract public function getAccessToken(); + +} diff --git a/3rdparty/Dropbox/OAuth/Consumer/Dropbox.php b/3rdparty/Dropbox/OAuth/Consumer/Dropbox.php new file mode 100644 index 0000000000..204a659de0 --- /dev/null +++ b/3rdparty/Dropbox/OAuth/Consumer/Dropbox.php @@ -0,0 +1,37 @@ +consumerRequest instanceof HTTP_OAuth_Consumer_Request) { + $this->consumerRequest = new HTTP_OAuth_Consumer_Request; + } + + // TODO: Change this and add in code to validate the SSL cert. + // see https://github.com/bagder/curl/blob/master/lib/mk-ca-bundle.pl + $this->consumerRequest->setConfig(array( + 'ssl_verify_peer' => false, + 'ssl_verify_host' => false + )); + + return $this->consumerRequest; + } +} diff --git a/3rdparty/Dropbox/OAuth/Curl.php b/3rdparty/Dropbox/OAuth/Curl.php new file mode 100644 index 0000000000..b75b27bb36 --- /dev/null +++ b/3rdparty/Dropbox/OAuth/Curl.php @@ -0,0 +1,282 @@ +consumerKey = $consumerKey; + $this->consumerSecret = $consumerSecret; + } + + /** + * Fetches a secured oauth url and returns the response body. + * + * @param string $uri + * @param mixed $arguments + * @param string $method + * @param array $httpHeaders + * @return string + */ + public function fetch($uri, $arguments = array(), $method = 'GET', $httpHeaders = array()) { + + $uri=str_replace('http://', 'https://', $uri); // all https, upload makes problems if not + if (is_string($arguments) and strtoupper($method) == 'POST') { + preg_match("/\?file=(.*)$/i", $uri, $matches); + if (isset($matches[1])) { + $uri = str_replace($matches[0], "", $uri); + $filename = $matches[1]; + $httpHeaders=array_merge($httpHeaders,$this->getOAuthHeader($uri, array("file" => $filename), $method)); + } + } else { + $httpHeaders=array_merge($httpHeaders,$this->getOAuthHeader($uri, $arguments, $method)); + } + $ch = curl_init(); + if (strtoupper($method) == 'POST') { + curl_setopt($ch, CURLOPT_URL, $uri); + curl_setopt($ch, CURLOPT_POST, true); +// if (is_array($arguments)) +// $arguments=http_build_query($arguments); + curl_setopt($ch, CURLOPT_POSTFIELDS, $arguments); +// $httpHeaders['Content-Length']=strlen($arguments); + } else { + curl_setopt($ch, CURLOPT_URL, $uri.'?'.http_build_query($arguments)); + curl_setopt($ch, CURLOPT_POST, false); + } + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_TIMEOUT, 300); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); +// curl_setopt($ch, CURLOPT_CAINFO, "rootca"); + curl_setopt($ch, CURLOPT_FRESH_CONNECT, true); + //Build header + $headers = array(); + foreach ($httpHeaders as $name => $value) { + $headers[] = "{$name}: $value"; + } + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); + if (!ini_get('safe_mode') && !ini_get('open_basedir')) + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true ); + if (function_exists($this->ProgressFunction) and defined('CURLOPT_PROGRESSFUNCTION')) { + curl_setopt($ch, CURLOPT_NOPROGRESS, false); + curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, $this->ProgressFunction); + curl_setopt($ch, CURLOPT_BUFFERSIZE, 512); + } + $response=curl_exec($ch); + $errorno=curl_errno($ch); + $error=curl_error($ch); + $status=curl_getinfo($ch,CURLINFO_HTTP_CODE); + curl_close($ch); + + + if (!empty($errorno)) + throw new Dropbox_Exception_NotFound('Curl error: ('.$errorno.') '.$error."\n"); + + if ($status>=300) { + $body = json_decode($response,true); + switch ($status) { + // Not modified + case 304 : + return array( + 'httpStatus' => 304, + 'body' => null, + ); + break; + case 403 : + throw new Dropbox_Exception_Forbidden('Forbidden. + This could mean a bad OAuth request, or a file or folder already existing at the target location. + ' . $body["error"] . "\n"); + case 404 : + throw new Dropbox_Exception_NotFound('Resource at uri: ' . $uri . ' could not be found. ' . + $body["error"] . "\n"); + case 507 : + throw new Dropbox_Exception_OverQuota('This dropbox is full. ' . + $body["error"] . "\n"); + } + if (!empty($body["error"])) + throw new Dropbox_Exception_RequestToken('Error: ('.$status.') '.$body["error"]."\n"); + } + + return array( + 'body' => $response, + 'httpStatus' => $status + ); + } + + /** + * Returns named array with oauth parameters for further use + * @return array Array with oauth_ parameters + */ + private function getOAuthBaseParams() { + $params['oauth_version'] = '1.0'; + $params['oauth_signature_method'] = 'HMAC-SHA1'; + + $params['oauth_consumer_key'] = $this->consumerKey; + $tokens = $this->getToken(); + if (isset($tokens['token']) && $tokens['token']) { + $params['oauth_token'] = $tokens['token']; + } + $params['oauth_timestamp'] = time(); + $params['oauth_nonce'] = md5(microtime() . mt_rand()); + return $params; + } + + /** + * Creates valid Authorization header for OAuth, based on URI and Params + * + * @param string $uri + * @param array $params + * @param string $method GET or POST, standard is GET + * @param array $oAuthParams optional, pass your own oauth_params here + * @return array Array for request's headers section like + * array('Authorization' => 'OAuth ...'); + */ + private function getOAuthHeader($uri, $params, $method = 'GET', $oAuthParams = null) { + $oAuthParams = $oAuthParams ? $oAuthParams : $this->getOAuthBaseParams(); + + // create baseString to encode for the sent parameters + $baseString = $method . '&'; + $baseString .= $this->oauth_urlencode($uri) . "&"; + + // OAuth header does not include GET-Parameters + $signatureParams = array_merge($params, $oAuthParams); + + // sorting the parameters + ksort($signatureParams); + + $encodedParams = array(); + foreach ($signatureParams as $key => $value) { + $encodedParams[] = $this->oauth_urlencode($key) . '=' . $this->oauth_urlencode($value); + } + + $baseString .= $this->oauth_urlencode(implode('&', $encodedParams)); + + // encode the signature + $tokens = $this->getToken(); + $hash = $this->hash_hmac_sha1($this->consumerSecret.'&'.$tokens['token_secret'], $baseString); + $signature = base64_encode($hash); + + // add signature to oAuthParams + $oAuthParams['oauth_signature'] = $signature; + + $oAuthEncoded = array(); + foreach ($oAuthParams as $key => $value) { + $oAuthEncoded[] = $key . '="' . $this->oauth_urlencode($value) . '"'; + } + + return array('Authorization' => 'OAuth ' . implode(', ', $oAuthEncoded)); + } + + /** + * Requests the OAuth request token. + * + * @return void + */ + public function getRequestToken() { + $result = $this->fetch(self::URI_REQUEST_TOKEN, array(), 'POST'); + if ($result['httpStatus'] == "200") { + $tokens = array(); + parse_str($result['body'], $tokens); + $this->setToken($tokens['oauth_token'], $tokens['oauth_token_secret']); + return $this->getToken(); + } else { + throw new Dropbox_Exception_RequestToken('We were unable to fetch request tokens. This likely means that your consumer key and/or secret are incorrect.'); + } + } + + /** + * Requests the OAuth access tokens. + * + * This method requires the 'unauthorized' request tokens + * and, if successful will set the authorized request tokens. + * + * @return void + */ + public function getAccessToken() { + $result = $this->fetch(self::URI_ACCESS_TOKEN, array(), 'POST'); + if ($result['httpStatus'] == "200") { + $tokens = array(); + parse_str($result['body'], $tokens); + $this->setToken($tokens['oauth_token'], $tokens['oauth_token_secret']); + return $this->getToken(); + } else { + throw new Dropbox_Exception_RequestToken('We were unable to fetch request tokens. This likely means that your consumer key and/or secret are incorrect.'); + } + } + + /** + * Helper function to properly urlencode parameters. + * See http://php.net/manual/en/function.oauth-urlencode.php + * + * @param string $string + * @return string + */ + private function oauth_urlencode($string) { + return str_replace('%E7', '~', rawurlencode($string)); + } + + /** + * Hash function for hmac_sha1; uses native function if available. + * + * @param string $key + * @param string $data + * @return string + */ + private function hash_hmac_sha1($key, $data) { + if (function_exists('hash_hmac') && in_array('sha1', hash_algos())) { + return hash_hmac('sha1', $data, $key, true); + } else { + $blocksize = 64; + $hashfunc = 'sha1'; + if (strlen($key) > $blocksize) { + $key = pack('H*', $hashfunc($key)); + } + + $key = str_pad($key, $blocksize, chr(0x00)); + $ipad = str_repeat(chr(0x36), $blocksize); + $opad = str_repeat(chr(0x5c), $blocksize); + $hash = pack('H*', $hashfunc(( $key ^ $opad ) . pack('H*', $hashfunc(($key ^ $ipad) . $data)))); + + return $hash; + } + } + + +} \ No newline at end of file diff --git a/3rdparty/Dropbox/README.md b/3rdparty/Dropbox/README.md new file mode 100644 index 0000000000..54e05db762 --- /dev/null +++ b/3rdparty/Dropbox/README.md @@ -0,0 +1,31 @@ +Dropbox-php +=========== + +This PHP library allows you to easily integrate dropbox with PHP. + +The following PHP extension is required: + +* json + +The library makes use of OAuth. At the moment you can use either of these libraries: + +[PHP OAuth extension](http://pecl.php.net/package/oauth) +[PEAR's HTTP_OAUTH package](http://pear.php.net/package/http_oauth) + +The extension is recommended, but if you can't install php extensions you should go for the pear package. +Installing +---------- + + pear channel-discover pear.dropbox-php.com + pear install dropbox-php/Dropbox-alpha + +Documentation +------------- +Check out the [documentation](http://www.dropbox-php.com/docs). + +Questions? +---------- + +[Dropbox-php Mailing list](http://groups.google.com/group/dropbox-php) +[Official Dropbox developer forum](http://forums.dropbox.com/forum.php?id=5) + diff --git a/3rdparty/Dropbox/autoload.php b/3rdparty/Dropbox/autoload.php new file mode 100644 index 0000000000..5388ea6334 --- /dev/null +++ b/3rdparty/Dropbox/autoload.php @@ -0,0 +1,29 @@ +key = $key; + $this->secret = $secret; + $this->callback_url = $callback_url; + }/*}}}*/ +}/*}}}*/ + +class OAuthToken {/*{{{*/ + // access tokens and request tokens + public $key; + public $secret; + + /** + * key = the token + * secret = the token secret + */ + function __construct($key, $secret) {/*{{{*/ + $this->key = $key; + $this->secret = $secret; + }/*}}}*/ + + /** + * generates the basic string serialization of a token that a server + * would respond to request_token and access_token calls with + */ + function to_string() {/*{{{*/ + return "oauth_token=" . OAuthUtil::urlencodeRFC3986($this->key) . + "&oauth_token_secret=" . OAuthUtil::urlencodeRFC3986($this->secret); + }/*}}}*/ + + function __toString() {/*{{{*/ + return $this->to_string(); + }/*}}}*/ +}/*}}}*/ + +class OAuthSignatureMethod {/*{{{*/ + public function check_signature(&$request, $consumer, $token, $signature) { + $built = $this->build_signature($request, $consumer, $token); + return $built == $signature; + } +}/*}}}*/ + +class OAuthSignatureMethod_HMAC_SHA1 extends OAuthSignatureMethod {/*{{{*/ + function get_name() {/*{{{*/ + return "HMAC-SHA1"; + }/*}}}*/ + + public function build_signature($request, $consumer, $token, $privKey=NULL) {/*{{{*/ + $base_string = $request->get_signature_base_string(); + $request->base_string = $base_string; + + $key_parts = array( + $consumer->secret, + ($token) ? $token->secret : "" + ); + + $key_parts = array_map(array('OAuthUtil','urlencodeRFC3986'), $key_parts); + $key = implode('&', $key_parts); + + return base64_encode( hash_hmac('sha1', $base_string, $key, true)); + }/*}}}*/ +}/*}}}*/ + +class OAuthSignatureMethod_RSA_SHA1 extends OAuthSignatureMethod {/*{{{*/ + public function get_name() {/*{{{*/ + return "RSA-SHA1"; + }/*}}}*/ + + protected function fetch_public_cert(&$request) {/*{{{*/ + // not implemented yet, ideas are: + // (1) do a lookup in a table of trusted certs keyed off of consumer + // (2) fetch via http using a url provided by the requester + // (3) some sort of specific discovery code based on request + // + // either way should return a string representation of the certificate + throw Exception("fetch_public_cert not implemented"); + }/*}}}*/ + + protected function fetch_private_cert($privKey) {//&$request) {/*{{{*/ + // not implemented yet, ideas are: + // (1) do a lookup in a table of trusted certs keyed off of consumer + // + // either way should return a string representation of the certificate + throw Exception("fetch_private_cert not implemented"); + }/*}}}*/ + + public function build_signature(&$request, $consumer, $token, $privKey) {/*{{{*/ + $base_string = $request->get_signature_base_string(); + + // Fetch the private key cert based on the request + //$cert = $this->fetch_private_cert($consumer->privKey); + + //Pull the private key ID from the certificate + //$privatekeyid = openssl_get_privatekey($cert); + + // hacked in + if ($privKey == '') { + $fp = fopen($GLOBALS['PRIV_KEY_FILE'], "r"); + $privKey = fread($fp, 8192); + fclose($fp); + } + $privatekeyid = openssl_get_privatekey($privKey); + + //Check the computer signature against the one passed in the query + $ok = openssl_sign($base_string, $signature, $privatekeyid); + + //Release the key resource + openssl_free_key($privatekeyid); + + return base64_encode($signature); + } /*}}}*/ + + public function check_signature(&$request, $consumer, $token, $signature) {/*{{{*/ + $decoded_sig = base64_decode($signature); + + $base_string = $request->get_signature_base_string(); + + // Fetch the public key cert based on the request + $cert = $this->fetch_public_cert($request); + + //Pull the public key ID from the certificate + $publickeyid = openssl_get_publickey($cert); + + //Check the computer signature against the one passed in the query + $ok = openssl_verify($base_string, $decoded_sig, $publickeyid); + + //Release the key resource + openssl_free_key($publickeyid); + + return $ok == 1; + } /*}}}*/ +}/*}}}*/ + +class OAuthRequest {/*{{{*/ + private $parameters; + private $http_method; + private $http_url; + // for debug purposes + public $base_string; + public static $version = '1.0'; + + function __construct($http_method, $http_url, $parameters=NULL) {/*{{{*/ + @$parameters or $parameters = array(); + $this->parameters = $parameters; + $this->http_method = $http_method; + $this->http_url = $http_url; + }/*}}}*/ + + + /** + * attempt to build up a request from what was passed to the server + */ + public static function from_request($http_method=NULL, $http_url=NULL, $parameters=NULL) {/*{{{*/ + $scheme = (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] != "on") ? 'http' : 'https'; + @$http_url or $http_url = $scheme . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; + @$http_method or $http_method = $_SERVER['REQUEST_METHOD']; + + $request_headers = OAuthRequest::get_headers(); + + // let the library user override things however they'd like, if they know + // which parameters to use then go for it, for example XMLRPC might want to + // do this + if ($parameters) { + $req = new OAuthRequest($http_method, $http_url, $parameters); + } + // next check for the auth header, we need to do some extra stuff + // if that is the case, namely suck in the parameters from GET or POST + // so that we can include them in the signature + else if (@substr($request_headers['Authorization'], 0, 5) == "OAuth") { + $header_parameters = OAuthRequest::split_header($request_headers['Authorization']); + if ($http_method == "GET") { + $req_parameters = $_GET; + } + else if ($http_method = "POST") { + $req_parameters = $_POST; + } + $parameters = array_merge($header_parameters, $req_parameters); + $req = new OAuthRequest($http_method, $http_url, $parameters); + } + else if ($http_method == "GET") { + $req = new OAuthRequest($http_method, $http_url, $_GET); + } + else if ($http_method == "POST") { + $req = new OAuthRequest($http_method, $http_url, $_POST); + } + return $req; + }/*}}}*/ + + /** + * pretty much a helper function to set up the request + */ + public static function from_consumer_and_token($consumer, $token, $http_method, $http_url, $parameters=NULL) {/*{{{*/ + @$parameters or $parameters = array(); + $defaults = array("oauth_version" => OAuthRequest::$version, + "oauth_nonce" => OAuthRequest::generate_nonce(), + "oauth_timestamp" => OAuthRequest::generate_timestamp(), + "oauth_consumer_key" => $consumer->key); + $parameters = array_merge($defaults, $parameters); + + if ($token) { + $parameters['oauth_token'] = $token->key; + } + + // oauth v1.0a + /*if (isset($_REQUEST['oauth_verifier'])) { + $parameters['oauth_verifier'] = $_REQUEST['oauth_verifier']; + }*/ + + + return new OAuthRequest($http_method, $http_url, $parameters); + }/*}}}*/ + + public function set_parameter($name, $value) {/*{{{*/ + $this->parameters[$name] = $value; + }/*}}}*/ + + public function get_parameter($name) {/*{{{*/ + return $this->parameters[$name]; + }/*}}}*/ + + public function get_parameters() {/*{{{*/ + return $this->parameters; + }/*}}}*/ + + /** + * Returns the normalized parameters of the request + * + * This will be all (except oauth_signature) parameters, + * sorted first by key, and if duplicate keys, then by + * value. + * + * The returned string will be all the key=value pairs + * concated by &. + * + * @return string + */ + public function get_signable_parameters() {/*{{{*/ + // Grab all parameters + $params = $this->parameters; + + // Remove oauth_signature if present + if (isset($params['oauth_signature'])) { + unset($params['oauth_signature']); + } + + // Urlencode both keys and values + $keys = array_map(array('OAuthUtil', 'urlencodeRFC3986'), array_keys($params)); + $values = array_map(array('OAuthUtil', 'urlencodeRFC3986'), array_values($params)); + $params = array_combine($keys, $values); + + // Sort by keys (natsort) + uksort($params, 'strnatcmp'); + +if(isset($params['title']) && isset($params['title-exact'])) { + $temp = $params['title-exact']; + $title = $params['title']; + + unset($params['title']); + unset($params['title-exact']); + + $params['title-exact'] = $temp; + $params['title'] = $title; +} + + // Generate key=value pairs + $pairs = array(); + foreach ($params as $key=>$value ) { + if (is_array($value)) { + // If the value is an array, it's because there are multiple + // with the same key, sort them, then add all the pairs + natsort($value); + foreach ($value as $v2) { + $pairs[] = $key . '=' . $v2; + } + } else { + $pairs[] = $key . '=' . $value; + } + } + + // Return the pairs, concated with & + return implode('&', $pairs); + }/*}}}*/ + + /** + * Returns the base string of this request + * + * The base string defined as the method, the url + * and the parameters (normalized), each urlencoded + * and the concated with &. + */ + public function get_signature_base_string() {/*{{{*/ + $parts = array( + $this->get_normalized_http_method(), + $this->get_normalized_http_url(), + $this->get_signable_parameters() + ); + + $parts = array_map(array('OAuthUtil', 'urlencodeRFC3986'), $parts); + + return implode('&', $parts); + }/*}}}*/ + + /** + * just uppercases the http method + */ + public function get_normalized_http_method() {/*{{{*/ + return strtoupper($this->http_method); + }/*}}}*/ + +/** + * parses the url and rebuilds it to be + * scheme://host/path + */ + public function get_normalized_http_url() { + $parts = parse_url($this->http_url); + + $scheme = (isset($parts['scheme'])) ? $parts['scheme'] : 'http'; + $port = (isset($parts['port'])) ? $parts['port'] : (($scheme == 'https') ? '443' : '80'); + $host = (isset($parts['host'])) ? strtolower($parts['host']) : ''; + $path = (isset($parts['path'])) ? $parts['path'] : ''; + + if (($scheme == 'https' && $port != '443') + || ($scheme == 'http' && $port != '80')) { + $host = "$host:$port"; + } + return "$scheme://$host$path"; + } + + /** + * builds a url usable for a GET request + */ + public function to_url() {/*{{{*/ + $out = $this->get_normalized_http_url() . "?"; + $out .= $this->to_postdata(); + return $out; + }/*}}}*/ + + /** + * builds the data one would send in a POST request + */ + public function to_postdata() {/*{{{*/ + $total = array(); + foreach ($this->parameters as $k => $v) { + $total[] = OAuthUtil::urlencodeRFC3986($k) . "=" . OAuthUtil::urlencodeRFC3986($v); + } + $out = implode("&", $total); + return $out; + }/*}}}*/ + + /** + * builds the Authorization: header + */ + public function to_header() {/*{{{*/ + $out ='Authorization: OAuth '; + $total = array(); + + /* + $sig = $this->parameters['oauth_signature']; + unset($this->parameters['oauth_signature']); + uksort($this->parameters, 'strnatcmp'); + $this->parameters['oauth_signature'] = $sig; + */ + + foreach ($this->parameters as $k => $v) { + if (substr($k, 0, 5) != "oauth") continue; + $out .= OAuthUtil::urlencodeRFC3986($k) . '="' . OAuthUtil::urlencodeRFC3986($v) . '", '; + } + $out = substr_replace($out, '', strlen($out) - 2); + + return $out; + }/*}}}*/ + + public function __toString() {/*{{{*/ + return $this->to_url(); + }/*}}}*/ + + + public function sign_request($signature_method, $consumer, $token, $privKey=NULL) {/*{{{*/ + $this->set_parameter("oauth_signature_method", $signature_method->get_name()); + $signature = $this->build_signature($signature_method, $consumer, $token, $privKey); + $this->set_parameter("oauth_signature", $signature); + }/*}}}*/ + + public function build_signature($signature_method, $consumer, $token, $privKey=NULL) {/*{{{*/ + $signature = $signature_method->build_signature($this, $consumer, $token, $privKey); + return $signature; + }/*}}}*/ + + /** + * util function: current timestamp + */ + private static function generate_timestamp() {/*{{{*/ + return time(); + }/*}}}*/ + + /** + * util function: current nonce + */ + private static function generate_nonce() {/*{{{*/ + $mt = microtime(); + $rand = mt_rand(); + + return md5($mt . $rand); // md5s look nicer than numbers + }/*}}}*/ + + /** + * util function for turning the Authorization: header into + * parameters, has to do some unescaping + */ + private static function split_header($header) {/*{{{*/ + // this should be a regex + // error cases: commas in parameter values + $parts = explode(",", $header); + $out = array(); + foreach ($parts as $param) { + $param = ltrim($param); + // skip the "realm" param, nobody ever uses it anyway + if (substr($param, 0, 5) != "oauth") continue; + + $param_parts = explode("=", $param); + + // rawurldecode() used because urldecode() will turn a "+" in the + // value into a space + $out[$param_parts[0]] = rawurldecode(substr($param_parts[1], 1, -1)); + } + return $out; + }/*}}}*/ + + /** + * helper to try to sort out headers for people who aren't running apache + */ + private static function get_headers() {/*{{{*/ + if (function_exists('apache_request_headers')) { + // we need this to get the actual Authorization: header + // because apache tends to tell us it doesn't exist + return apache_request_headers(); + } + // otherwise we don't have apache and are just going to have to hope + // that $_SERVER actually contains what we need + $out = array(); + foreach ($_SERVER as $key => $value) { + if (substr($key, 0, 5) == "HTTP_") { + // this is chaos, basically it is just there to capitalize the first + // letter of every word that is not an initial HTTP and strip HTTP + // code from przemek + $key = str_replace(" ", "-", ucwords(strtolower(str_replace("_", " ", substr($key, 5))))); + $out[$key] = $value; + } + } + return $out; + }/*}}}*/ +}/*}}}*/ + +class OAuthServer {/*{{{*/ + protected $timestamp_threshold = 300; // in seconds, five minutes + protected $version = 1.0; // hi blaine + protected $signature_methods = array(); + + protected $data_store; + + function __construct($data_store) {/*{{{*/ + $this->data_store = $data_store; + }/*}}}*/ + + public function add_signature_method($signature_method) {/*{{{*/ + $this->signature_methods[$signature_method->get_name()] = + $signature_method; + }/*}}}*/ + + // high level functions + + /** + * process a request_token request + * returns the request token on success + */ + public function fetch_request_token(&$request) {/*{{{*/ + $this->get_version($request); + + $consumer = $this->get_consumer($request); + + // no token required for the initial token request + $token = NULL; + + $this->check_signature($request, $consumer, $token); + + $new_token = $this->data_store->new_request_token($consumer); + + return $new_token; + }/*}}}*/ + + /** + * process an access_token request + * returns the access token on success + */ + public function fetch_access_token(&$request) {/*{{{*/ + $this->get_version($request); + + $consumer = $this->get_consumer($request); + + // requires authorized request token + $token = $this->get_token($request, $consumer, "request"); + + $this->check_signature($request, $consumer, $token); + + $new_token = $this->data_store->new_access_token($token, $consumer); + + return $new_token; + }/*}}}*/ + + /** + * verify an api call, checks all the parameters + */ + public function verify_request(&$request) {/*{{{*/ + $this->get_version($request); + $consumer = $this->get_consumer($request); + $token = $this->get_token($request, $consumer, "access"); + $this->check_signature($request, $consumer, $token); + return array($consumer, $token); + }/*}}}*/ + + // Internals from here + /** + * version 1 + */ + private function get_version(&$request) {/*{{{*/ + $version = $request->get_parameter("oauth_version"); + if (!$version) { + $version = 1.0; + } + if ($version && $version != $this->version) { + throw new OAuthException("OAuth version '$version' not supported"); + } + return $version; + }/*}}}*/ + + /** + * figure out the signature with some defaults + */ + private function get_signature_method(&$request) {/*{{{*/ + $signature_method = + @$request->get_parameter("oauth_signature_method"); + if (!$signature_method) { + $signature_method = "PLAINTEXT"; + } + if (!in_array($signature_method, + array_keys($this->signature_methods))) { + throw new OAuthException( + "Signature method '$signature_method' not supported try one of the following: " . implode(", ", array_keys($this->signature_methods)) + ); + } + return $this->signature_methods[$signature_method]; + }/*}}}*/ + + /** + * try to find the consumer for the provided request's consumer key + */ + private function get_consumer(&$request) {/*{{{*/ + $consumer_key = @$request->get_parameter("oauth_consumer_key"); + if (!$consumer_key) { + throw new OAuthException("Invalid consumer key"); + } + + $consumer = $this->data_store->lookup_consumer($consumer_key); + if (!$consumer) { + throw new OAuthException("Invalid consumer"); + } + + return $consumer; + }/*}}}*/ + + /** + * try to find the token for the provided request's token key + */ + private function get_token(&$request, $consumer, $token_type="access") {/*{{{*/ + $token_field = @$request->get_parameter('oauth_token'); + $token = $this->data_store->lookup_token( + $consumer, $token_type, $token_field + ); + if (!$token) { + throw new OAuthException("Invalid $token_type token: $token_field"); + } + return $token; + }/*}}}*/ + + /** + * all-in-one function to check the signature on a request + * should guess the signature method appropriately + */ + private function check_signature(&$request, $consumer, $token) {/*{{{*/ + // this should probably be in a different method + $timestamp = @$request->get_parameter('oauth_timestamp'); + $nonce = @$request->get_parameter('oauth_nonce'); + + $this->check_timestamp($timestamp); + $this->check_nonce($consumer, $token, $nonce, $timestamp); + + $signature_method = $this->get_signature_method($request); + + $signature = $request->get_parameter('oauth_signature'); + $valid_sig = $signature_method->check_signature( + $request, + $consumer, + $token, + $signature + ); + + if (!$valid_sig) { + throw new OAuthException("Invalid signature"); + } + }/*}}}*/ + + /** + * check that the timestamp is new enough + */ + private function check_timestamp($timestamp) {/*{{{*/ + // verify that timestamp is recentish + $now = time(); + if ($now - $timestamp > $this->timestamp_threshold) { + throw new OAuthException("Expired timestamp, yours $timestamp, ours $now"); + } + }/*}}}*/ + + /** + * check that the nonce is not repeated + */ + private function check_nonce($consumer, $token, $nonce, $timestamp) {/*{{{*/ + // verify that the nonce is uniqueish + $found = $this->data_store->lookup_nonce($consumer, $token, $nonce, $timestamp); + if ($found) { + throw new OAuthException("Nonce already used: $nonce"); + } + }/*}}}*/ + + + +}/*}}}*/ + +class OAuthDataStore {/*{{{*/ + function lookup_consumer($consumer_key) {/*{{{*/ + // implement me + }/*}}}*/ + + function lookup_token($consumer, $token_type, $token) {/*{{{*/ + // implement me + }/*}}}*/ + + function lookup_nonce($consumer, $token, $nonce, $timestamp) {/*{{{*/ + // implement me + }/*}}}*/ + + function fetch_request_token($consumer) {/*{{{*/ + // return a new token attached to this consumer + }/*}}}*/ + + function fetch_access_token($token, $consumer) {/*{{{*/ + // return a new access token attached to this consumer + // for the user associated with this token if the request token + // is authorized + // should also invalidate the request token + }/*}}}*/ + +}/*}}}*/ + + +/* A very naive dbm-based oauth storage + */ +class SimpleOAuthDataStore extends OAuthDataStore {/*{{{*/ + private $dbh; + + function __construct($path = "oauth.gdbm") {/*{{{*/ + $this->dbh = dba_popen($path, 'c', 'gdbm'); + }/*}}}*/ + + function __destruct() {/*{{{*/ + dba_close($this->dbh); + }/*}}}*/ + + function lookup_consumer($consumer_key) {/*{{{*/ + $rv = dba_fetch("consumer_$consumer_key", $this->dbh); + if ($rv === FALSE) { + return NULL; + } + $obj = unserialize($rv); + if (!($obj instanceof OAuthConsumer)) { + return NULL; + } + return $obj; + }/*}}}*/ + + function lookup_token($consumer, $token_type, $token) {/*{{{*/ + $rv = dba_fetch("${token_type}_${token}", $this->dbh); + if ($rv === FALSE) { + return NULL; + } + $obj = unserialize($rv); + if (!($obj instanceof OAuthToken)) { + return NULL; + } + return $obj; + }/*}}}*/ + + function lookup_nonce($consumer, $token, $nonce, $timestamp) {/*{{{*/ + return dba_exists("nonce_$nonce", $this->dbh); + }/*}}}*/ + + function new_token($consumer, $type="request") {/*{{{*/ + $key = md5(time()); + $secret = time() + time(); + $token = new OAuthToken($key, md5(md5($secret))); + if (!dba_insert("${type}_$key", serialize($token), $this->dbh)) { + throw new OAuthException("doooom!"); + } + return $token; + }/*}}}*/ + + function new_request_token($consumer) {/*{{{*/ + return $this->new_token($consumer, "request"); + }/*}}}*/ + + function new_access_token($token, $consumer) {/*{{{*/ + + $token = $this->new_token($consumer, 'access'); + dba_delete("request_" . $token->key, $this->dbh); + return $token; + }/*}}}*/ +}/*}}}*/ + +class OAuthUtil {/*{{{*/ + public static function urlencodeRFC3986($string) {/*{{{*/ + return str_replace('%7E', '~', rawurlencode($string)); + }/*}}}*/ + + public static function urldecodeRFC3986($string) {/*{{{*/ + return rawurldecode($string); + }/*}}}*/ +}/*}}}*/ + +?> \ No newline at end of file diff --git a/3rdparty/Google/common.inc.php b/3rdparty/Google/common.inc.php new file mode 100755 index 0000000000..57185cdc4d --- /dev/null +++ b/3rdparty/Google/common.inc.php @@ -0,0 +1,185 @@ + + */ + +$PRIV_KEY_FILE = '/path/to/your/rsa_private_key.pem'; + +// OAuth library - http://oauth.googlecode.com/svn/code/php/ +require_once('OAuth.php'); + +// Google's accepted signature methods +$hmac_method = new OAuthSignatureMethod_HMAC_SHA1(); +$rsa_method = new OAuthSignatureMethod_RSA_SHA1(); +$SIG_METHODS = array($rsa_method->get_name() => $rsa_method, + $hmac_method->get_name() => $hmac_method); + +/** + * Makes an HTTP request to the specified URL + * + * @param string $http_method The HTTP method (GET, POST, PUT, DELETE) + * @param string $url Full URL of the resource to access + * @param array $extraHeaders (optional) Additional headers to include in each + * request. Elements are header/value pair strings ('Host: example.com') + * @param string $postData (optional) POST/PUT request body + * @param bool $returnResponseHeaders True if resp. headers should be returned. + * @return string Response body from the server + */ +function send_signed_request($http_method, $url, $extraHeaders=null, + $postData=null, $returnResponseHeaders=true) { + $curl = curl_init($url); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); + curl_setopt($curl, CURLOPT_FAILONERROR, false); + curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); + + // Return request headers in the reponse +// curl_setopt($curl, CURLINFO_HEADER_OUT, true); + + // Return response headers ni the response? + if ($returnResponseHeaders) { + curl_setopt($curl, CURLOPT_HEADER, true); + } + + $headers = array(); + //$headers[] = 'GData-Version: 2.0'; // use GData v2 by default + if (is_array($extraHeaders)) { + $headers = array_merge($headers, $extraHeaders); + } + + // Setup default curl options for each type of HTTP request. + // This is also a great place to add additional headers for each request. + switch($http_method) { + case 'GET': + curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); + break; + case 'POST': + curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); + curl_setopt($curl, CURLOPT_POST, 1); + curl_setopt($curl, CURLOPT_POSTFIELDS, $postData); + break; + case 'PUT': + $headers[] = 'If-Match: *'; + curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); + curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $http_method); + curl_setopt($curl, CURLOPT_POSTFIELDS, $postData); + break; + case 'DELETE': + $headers[] = 'If-Match: *'; + curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); + curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $http_method); + break; + default: + curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); + } + + // Execute the request. If an error occures, fill the response body with it. + $response = curl_exec($curl); + if (!$response) { + $response = curl_error($curl); + } + + // Add server's response headers to our response body + $response = curl_getinfo($curl, CURLINFO_HEADER_OUT) . $response; + + curl_close($curl); + + return $response; +} + +/** +* Takes XML as a string and returns it nicely indented +* +* @param string $xml The xml to beautify +* @param boolean $html_output True if returned XML should be escaped for HTML. +* @return string The beautified xml +*/ +function xml_pretty_printer($xml, $html_output=false) { + $xml_obj = new SimpleXMLElement($xml); + $level = 2; + + // Get an array containing each XML element + $xml = explode("\n", preg_replace('/>\s*\n<", $xml_obj->asXML())); + + // Hold current indentation level + $indent = 0; + + $pretty = array(); + + // Shift off opening XML tag if present + if (count($xml) && preg_match('/^<\?\s*xml/', $xml[0])) { + $pretty[] = array_shift($xml); + } + + foreach ($xml as $el) { + if (preg_match('/^<([\w])+[^>\/]*>$/U', $el)) { + // opening tag, increase indent + $pretty[] = str_repeat(' ', $indent) . $el; + $indent += $level; + } else { + if (preg_match('/^<\/.+>$/', $el)) { + $indent -= $level; // closing tag, decrease indent + } + if ($indent < 0) { + $indent += $level; + } + $pretty[] = str_repeat(' ', $indent) . $el; + } + } + + $xml = implode("\n", $pretty); + return $html_output ? htmlentities($xml) : $xml; +} + +/** + * Joins key/value pairs by $inner_glue and each pair together by $outer_glue. + * + * Example: implode_assoc('=', '&', array('a' => 1, 'b' => 2)) === 'a=1&b=2' + * + * @param string $inner_glue What to implode each key/value pair with + * @param string $outer_glue What to impode each key/value string subset with + * @param array $array Associative array of query parameters + * @return string Urlencoded string of query parameters + */ +function implode_assoc($inner_glue, $outer_glue, $array) { + $output = array(); + foreach($array as $key => $item) { + $output[] = $key . $inner_glue . urlencode($item); + } + return implode($outer_glue, $output); +} + +/** + * Explodes a string of key/value url parameters into an associative array. + * This method performs the compliment operations of implode_assoc(). + * + * Example: explode_assoc('=', '&', 'a=1&b=2') === array('a' => 1, 'b' => 2) + * + * @param string $inner_glue What each key/value pair is joined with + * @param string $outer_glue What each set of key/value pairs is joined with. + * @param array $array Associative array of query parameters + * @return array Urlencoded string of query parameters + */ +function explode_assoc($inner_glue, $outer_glue, $params) { + $tempArr = explode($outer_glue, $params); + foreach($tempArr as $val) { + $pos = strpos($val, $inner_glue); + $key = substr($val, 0, $pos); + $array2[$key] = substr($val, $pos + 1, strlen($val)); + } + return $array2; +} + +?> \ No newline at end of file diff --git a/3rdparty/MDB2/Schema/Parser.php b/3rdparty/MDB2/Schema/Parser.php index cfd0c37d8a..3c4345661b 100644 --- a/3rdparty/MDB2/Schema/Parser.php +++ b/3rdparty/MDB2/Schema/Parser.php @@ -146,33 +146,6 @@ class MDB2_Schema_Parser extends XML_Parser ); } - /** - * PHP 4 compatible constructor - * - * @param array $variables mixed array with user defined schema - * variables - * @param bool $fail_on_invalid_names array with reserved words per RDBMS - * @param array $structure multi dimensional array with - * database schema and data - * @param array $valid_types information of all valid fields - * types - * @param bool $force_defaults if true sets a default value to - * field when not explicit - * @param int $max_identifiers_length maximum allowed size for entities - * name - * - * @return void - * - * @access public - * @static - */ - function MDB2_Schema_Parser($variables, $fail_on_invalid_names = true, - $structure = false, $valid_types = array(), $force_defaults = true, - $max_identifiers_length = null - ) { - $this->__construct($variables, $fail_on_invalid_names, $structure, $valid_types, $force_defaults); - } - /** * Triggered when reading a XML open tag * diff --git a/3rdparty/MDB2/Schema/Parser2.php b/3rdparty/MDB2/Schema/Parser2.php index b415b4a336..f27dffbabf 100644 --- a/3rdparty/MDB2/Schema/Parser2.php +++ b/3rdparty/MDB2/Schema/Parser2.php @@ -143,33 +143,6 @@ class MDB2_Schema_Parser2 extends XML_Unserializer parent::XML_Unserializer($this->options); } - /** - * PHP 4 compatible constructor - * - * @param array $variables mixed array with user defined schema - * variables - * @param bool $fail_on_invalid_names array with reserved words per RDBMS - * @param array $structure multi dimensional array with - * database schema and data - * @param array $valid_types information of all valid fields - * types - * @param bool $force_defaults if true sets a default value to - * field when not explicit - * @param int $max_identifiers_length maximum allowed size for entities - * name - * - * @return void - * - * @access public - * @static - */ - function MDB2_Schema_Parser2($variables, $fail_on_invalid_names = true, - $structure = false, $valid_types = array(), $force_defaults = true, - $max_identifiers_length = null - ) { - $this->__construct($variables, $fail_on_invalid_names, $structure, $valid_types, $force_defaults); - } - /** * Main method. Parses XML Schema File. * diff --git a/3rdparty/MDB2/Schema/Validate.php b/3rdparty/MDB2/Schema/Validate.php index 4cff175576..4a8e0d27ba 100644 --- a/3rdparty/MDB2/Schema/Validate.php +++ b/3rdparty/MDB2/Schema/Validate.php @@ -108,28 +108,6 @@ class MDB2_Schema_Validate $this->max_identifiers_length = $max_identifiers_length; } - /** - * PHP 4 compatible constructor - * - * @param bool $fail_on_invalid_names array with reserved words per RDBMS - * @param array $valid_types information of all valid fields - * types - * @param bool $force_defaults if true sets a default value to - * field when not explicit - * @param int $max_identifiers_length maximum allowed size for entities - * name - * - * @return void - * - * @access public - * @static - */ - function MDB2_Schema_Validate($fail_on_invalid_names = true, $valid_types = array(), - $force_defaults = true, $max_identifiers_length = null - ) { - $this->__construct($fail_on_invalid_names, $valid_types, $force_defaults); - } - // }}} // {{{ raiseError() diff --git a/3rdparty/MDB2/Schema/Writer.php b/3rdparty/MDB2/Schema/Writer.php index 70a03168de..3eaa39a207 100644 --- a/3rdparty/MDB2/Schema/Writer.php +++ b/3rdparty/MDB2/Schema/Writer.php @@ -82,22 +82,6 @@ class MDB2_Schema_Writer $this->valid_types = $valid_types; } - /** - * PHP 4 compatible constructor - * - * @param array $valid_types information of all valid fields - * types - * - * @return void - * - * @access public - * @static - */ - function MDB2_Schema_Writer($valid_types = array()) - { - $this->__construct($valid_types); - } - // }}} // {{{ raiseError() diff --git a/3rdparty/Sabre.includes.php b/3rdparty/Sabre.includes.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/CalDAV/Backend/Abstract.php b/3rdparty/Sabre/CalDAV/Backend/Abstract.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/CalDAV/Backend/PDO.php b/3rdparty/Sabre/CalDAV/Backend/PDO.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/CalDAV/Calendar.php b/3rdparty/Sabre/CalDAV/Calendar.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/CalDAV/CalendarObject.php b/3rdparty/Sabre/CalDAV/CalendarObject.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/CalDAV/CalendarQueryParser.php b/3rdparty/Sabre/CalDAV/CalendarQueryParser.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/CalDAV/CalendarQueryValidator.php b/3rdparty/Sabre/CalDAV/CalendarQueryValidator.php old mode 100644 new mode 100755 index 1bb6b5d53f..8f674840e8 --- a/3rdparty/Sabre/CalDAV/CalendarQueryValidator.php +++ b/3rdparty/Sabre/CalDAV/CalendarQueryValidator.php @@ -294,6 +294,7 @@ class Sabre_CalDAV_CalendarQueryValidator { // in the VALARM component code, so this is a hack, and an // expensive one too. if ($component->parent->name === 'VEVENT' && $component->parent->RRULE) { + // Fire up the iterator! $it = new Sabre_VObject_RecurrenceIterator($component->parent->parent, (string)$component->parent->UID); while($it->valid()) { @@ -303,15 +304,37 @@ class Sabre_CalDAV_CalendarQueryValidator { // one is the first to trigger. Based on this, we can // determine if we can 'give up' expanding events. $firstAlarm = null; - foreach($expandedEvent->VALARM as $expandedAlarm) { - $effectiveTrigger = $expandedAlarm->getEffectiveTriggerTime(); - if (!$firstAlarm || $effectiveTrigger < $firstAlarm) { - $firstAlarm = $effectiveTrigger; - } - if ($expandedAlarm->isInTimeRange($start, $end)) { - return true; - } + if ($expandedEvent->VALARM !== null) { + foreach($expandedEvent->VALARM as $expandedAlarm) { + $effectiveTrigger = $expandedAlarm->getEffectiveTriggerTime(); + if ($expandedAlarm->isInTimeRange($start, $end)) { + return true; + } + + if ((string)$expandedAlarm->TRIGGER['VALUE'] === 'DATE-TIME') { + // This is an alarm with a non-relative trigger + // time, likely created by a buggy client. The + // implication is that every alarm in this + // recurring event trigger at the exact same + // time. It doesn't make sense to traverse + // further. + } else { + // We store the first alarm as a means to + // figure out when we can stop traversing. + if (!$firstAlarm || $effectiveTrigger < $firstAlarm) { + $firstAlarm = $effectiveTrigger; + } + } + } + } + if (is_null($firstAlarm)) { + // No alarm was found. + // + // Or technically: No alarm that will change for + // every instance of the recurrence was found, + // which means we can assume there was no match. + return false; } if ($firstAlarm > $end) { return false; diff --git a/3rdparty/Sabre/CalDAV/CalendarRootNode.php b/3rdparty/Sabre/CalDAV/CalendarRootNode.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/CalDAV/ICSExportPlugin.php b/3rdparty/Sabre/CalDAV/ICSExportPlugin.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/CalDAV/ICalendar.php b/3rdparty/Sabre/CalDAV/ICalendar.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/CalDAV/ICalendarObject.php b/3rdparty/Sabre/CalDAV/ICalendarObject.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/CalDAV/Plugin.php b/3rdparty/Sabre/CalDAV/Plugin.php old mode 100644 new mode 100755 index d7d1d97051..c56ab38484 --- a/3rdparty/Sabre/CalDAV/Plugin.php +++ b/3rdparty/Sabre/CalDAV/Plugin.php @@ -49,23 +49,23 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin { /** * The email handler for invites and other scheduling messages. - * - * @var Sabre_CalDAV_Schedule_IMip + * + * @var Sabre_CalDAV_Schedule_IMip */ protected $imipHandler; /** * Sets the iMIP handler. * - * iMIP = The email transport of iCalendar scheduling messages. Setting - * this is optional, but if you want the server to allow invites to be sent + * iMIP = The email transport of iCalendar scheduling messages. Setting + * this is optional, but if you want the server to allow invites to be sent * out, you must set a handler. * - * Specifically iCal will plain assume that the server supports this. If - * the server doesn't, iCal will display errors when inviting people to + * Specifically iCal will plain assume that the server supports this. If + * the server doesn't, iCal will display errors when inviting people to * events. * - * @param Sabre_CalDAV_Schedule_IMip $imipHandler + * @param Sabre_CalDAV_Schedule_IMip $imipHandler * @return void */ public function setIMipHandler(Sabre_CalDAV_Schedule_IMip $imipHandler) { @@ -672,6 +672,42 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin { } + if ($vobj->name !== 'VCALENDAR') { + throw new Sabre_DAV_Exception_UnsupportedMediaType('This collection can only support iCalendar objects.'); + } + + $foundType = null; + $foundUID = null; + foreach($vobj->getComponents() as $component) { + switch($component->name) { + case 'VTIMEZONE' : + continue 2; + case 'VEVENT' : + case 'VTODO' : + case 'VJOURNAL' : + if (is_null($foundType)) { + $foundType = $component->name; + if (!isset($component->UID)) { + throw new Sabre_DAV_Exception_BadRequest('Every ' . $component->name . ' component must have an UID'); + } + $foundUID = (string)$component->UID; + } else { + if ($foundType !== $component->name) { + throw new Sabre_DAV_Exception_BadRequest('A calendar object must only contain 1 component. We found a ' . $component->name . ' as well as a ' . $foundType); + } + if ($foundUID !== (string)$component->UID) { + throw new Sabre_DAV_Exception_BadRequest('Every ' . $component->name . ' in this object must have identical UIDs'); + } + } + break; + default : + throw new Sabre_DAV_Exception_BadRequest('You are not allowed to create components of type: ' . $component->name . ' here'); + + } + } + if (!$foundType) + throw new Sabre_DAV_Exception_BadRequest('iCalendar object must contain at least 1 of VEVENT, VTODO or VJOURNAL'); + } /** @@ -687,12 +723,12 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin { if (!$originator) { throw new Sabre_DAV_Exception_BadRequest('The Originator: header must be specified when making POST requests'); - } + } if (!$recipients) { throw new Sabre_DAV_Exception_BadRequest('The Recipient: header must be specified when making POST requests'); - } + } - if (!preg_match('/^mailto:(.*)@(.*)$/', $originator)) { + if (!preg_match('/^mailto:(.*)@(.*)$/i', $originator)) { throw new Sabre_DAV_Exception_BadRequest('Originator must start with mailto: and must be valid email address'); } $originator = substr($originator,7); @@ -701,14 +737,14 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin { foreach($recipients as $k=>$recipient) { $recipient = trim($recipient); - if (!preg_match('/^mailto:(.*)@(.*)$/', $recipient)) { + if (!preg_match('/^mailto:(.*)@(.*)$/i', $recipient)) { throw new Sabre_DAV_Exception_BadRequest('Recipients must start with mailto: and must be valid email address'); } $recipient = substr($recipient, 7); $recipients[$k] = $recipient; } - // We need to make sure that 'originator' matches one of the email + // We need to make sure that 'originator' matches one of the email // addresses of the selected principal. $principal = $outboxNode->getOwner(); $props = $this->server->getProperties($principal,array( @@ -724,7 +760,7 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin { throw new Sabre_DAV_Exception_Forbidden('The addresses specified in the Originator header did not match any addresses in the owners calendar-user-address-set header'); } - try { + try { $vObject = Sabre_VObject_Reader::read($this->server->httpRequest->getBody(true)); } catch (Sabre_VObject_ParseException $e) { throw new Sabre_DAV_Exception_BadRequest('The request body must be a valid iCalendar object. Parse error: ' . $e->getMessage()); @@ -749,9 +785,10 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin { } if (in_array($method, array('REQUEST','REPLY','ADD','CANCEL')) && $componentType==='VEVENT') { - $this->iMIPMessage($originator, $recipients, $vObject); + $result = $this->iMIPMessage($originator, $recipients, $vObject); $this->server->httpResponse->sendStatus(200); - $this->server->httpResponse->sendBody('Messages sent'); + $this->server->httpResponse->setHeader('Content-Type','application/xml'); + $this->server->httpResponse->sendBody($this->generateScheduleResponse($result)); } else { throw new Sabre_DAV_Exception_NotImplemented('This iTIP method is currently not implemented'); } @@ -760,18 +797,83 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin { /** * Sends an iMIP message by email. - * - * @param string $originator - * @param array $recipients - * @param Sabre_VObject_Component $vObject - * @return void + * + * This method must return an array with status codes per recipient. + * This should look something like: + * + * array( + * 'user1@example.org' => '2.0;Success' + * ) + * + * Formatting for this status code can be found at: + * https://tools.ietf.org/html/rfc5545#section-3.8.8.3 + * + * A list of valid status codes can be found at: + * https://tools.ietf.org/html/rfc5546#section-3.6 + * + * @param string $originator + * @param array $recipients + * @param Sabre_VObject_Component $vObject + * @return array */ protected function iMIPMessage($originator, array $recipients, Sabre_VObject_Component $vObject) { if (!$this->imipHandler) { - throw new Sabre_DAV_Exception_NotImplemented('No iMIP handler is setup on this server.'); + $resultStatus = '5.2;This server does not support this operation'; + } else { + $this->imipHandler->sendMessage($originator, $recipients, $vObject); + $resultStatus = '2.0;Success'; } - $this->imipHandler->sendMessage($originator, $recipients, $vObject); + + $result = array(); + foreach($recipients as $recipient) { + $result[$recipient] = $resultStatus; + } + + return $result; + + + } + + /** + * Generates a schedule-response XML body + * + * The recipients array is a key->value list, containing email addresses + * and iTip status codes. See the iMIPMessage method for a description of + * the value. + * + * @param array $recipients + * @return string + */ + public function generateScheduleResponse(array $recipients) { + + $dom = new DOMDocument('1.0','utf-8'); + $dom->formatOutput = true; + $xscheduleResponse = $dom->createElement('cal:schedule-response'); + $dom->appendChild($xscheduleResponse); + + foreach($this->server->xmlNamespaces as $namespace=>$prefix) { + + $xscheduleResponse->setAttribute('xmlns:' . $prefix, $namespace); + + } + + foreach($recipients as $recipient=>$status) { + $xresponse = $dom->createElement('cal:response'); + + $xrecipient = $dom->createElement('cal:recipient'); + $xrecipient->appendChild($dom->createTextNode($recipient)); + $xresponse->appendChild($xrecipient); + + $xrequestStatus = $dom->createElement('cal:request-status'); + $xrequestStatus->appendChild($dom->createTextNode($status)); + $xresponse->appendChild($xrequestStatus); + + $xscheduleResponse->appendChild($xresponse); + + } + + return $dom->saveXML(); } diff --git a/3rdparty/Sabre/CalDAV/Principal/Collection.php b/3rdparty/Sabre/CalDAV/Principal/Collection.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/CalDAV/Principal/ProxyRead.php b/3rdparty/Sabre/CalDAV/Principal/ProxyRead.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/CalDAV/Principal/ProxyWrite.php b/3rdparty/Sabre/CalDAV/Principal/ProxyWrite.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/CalDAV/Principal/User.php b/3rdparty/Sabre/CalDAV/Principal/User.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php b/3rdparty/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/CalDAV/Property/SupportedCalendarData.php b/3rdparty/Sabre/CalDAV/Property/SupportedCalendarData.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/CalDAV/Property/SupportedCollationSet.php b/3rdparty/Sabre/CalDAV/Property/SupportedCollationSet.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/CalDAV/Schedule/IMip.php b/3rdparty/Sabre/CalDAV/Schedule/IMip.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/CalDAV/Schedule/IOutbox.php b/3rdparty/Sabre/CalDAV/Schedule/IOutbox.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/CalDAV/Schedule/Outbox.php b/3rdparty/Sabre/CalDAV/Schedule/Outbox.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/CalDAV/Server.php b/3rdparty/Sabre/CalDAV/Server.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/CalDAV/UserCalendars.php b/3rdparty/Sabre/CalDAV/UserCalendars.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/CalDAV/Version.php b/3rdparty/Sabre/CalDAV/Version.php old mode 100644 new mode 100755 index 939e903c89..ace9901c08 --- a/3rdparty/Sabre/CalDAV/Version.php +++ b/3rdparty/Sabre/CalDAV/Version.php @@ -14,7 +14,7 @@ class Sabre_CalDAV_Version { /** * Full version number */ - const VERSION = '1.6.2'; + const VERSION = '1.6.4'; /** * Stability : alpha, beta, stable diff --git a/3rdparty/Sabre/CalDAV/includes.php b/3rdparty/Sabre/CalDAV/includes.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/CardDAV/AddressBook.php b/3rdparty/Sabre/CardDAV/AddressBook.php old mode 100644 new mode 100755 index 3b381e1eea..12297175a8 --- a/3rdparty/Sabre/CardDAV/AddressBook.php +++ b/3rdparty/Sabre/CardDAV/AddressBook.php @@ -108,7 +108,9 @@ class Sabre_CardDAV_AddressBook extends Sabre_DAV_Collection implements Sabre_Ca */ public function createFile($name,$vcardData = null) { - $vcardData = stream_get_contents($vcardData); + if (is_resource($vcardData)) { + $vcardData = stream_get_contents($vcardData); + } // Converting to UTF-8, if needed $vcardData = Sabre_DAV_StringUtil::ensureUTF8($vcardData); diff --git a/3rdparty/Sabre/CardDAV/AddressBookQueryParser.php b/3rdparty/Sabre/CardDAV/AddressBookQueryParser.php old mode 100644 new mode 100755 index 85a4963127..46bb8ff18d --- a/3rdparty/Sabre/CardDAV/AddressBookQueryParser.php +++ b/3rdparty/Sabre/CardDAV/AddressBookQueryParser.php @@ -9,7 +9,7 @@ * @package Sabre * @subpackage CardDAV * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_CardDAV_AddressBookQueryParser { @@ -88,12 +88,22 @@ class Sabre_CardDAV_AddressBookQueryParser { if (is_nan($limit)) $limit = null; $filter = $this->xpath->query('/card:addressbook-query/card:filter'); - if ($filter->length !== 1) { + + // According to the CardDAV spec there needs to be exactly 1 filter + // element. However, KDE 4.8.2 contains a bug that will encode 0 filter + // elements, so this is a workaround for that. + // + // See: https://bugs.kde.org/show_bug.cgi?id=300047 + if ($filter->length === 0) { + $test = null; + $filter = null; + } elseif ($filter->length === 1) { + $filter = $filter->item(0); + $test = $this->xpath->evaluate('string(@test)', $filter); + } else { throw new Sabre_DAV_Exception_BadRequest('Only one filter element is allowed'); } - $filter = $filter->item(0); - $test = $this->xpath->evaluate('string(@test)', $filter); if (!$test) $test = self::TEST_ANYOF; if ($test !== self::TEST_ANYOF && $test !== self::TEST_ALLOF) { throw new Sabre_DAV_Exception_BadRequest('The test attribute must either hold "anyof" or "allof"'); diff --git a/3rdparty/Sabre/CardDAV/AddressBookRoot.php b/3rdparty/Sabre/CardDAV/AddressBookRoot.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/CardDAV/Backend/Abstract.php b/3rdparty/Sabre/CardDAV/Backend/Abstract.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/CardDAV/Backend/PDO.php b/3rdparty/Sabre/CardDAV/Backend/PDO.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/CardDAV/Card.php b/3rdparty/Sabre/CardDAV/Card.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/CardDAV/IAddressBook.php b/3rdparty/Sabre/CardDAV/IAddressBook.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/CardDAV/ICard.php b/3rdparty/Sabre/CardDAV/ICard.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/CardDAV/IDirectory.php b/3rdparty/Sabre/CardDAV/IDirectory.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/CardDAV/Plugin.php b/3rdparty/Sabre/CardDAV/Plugin.php old mode 100644 new mode 100755 index 095769fedd..96def6dd96 --- a/3rdparty/Sabre/CardDAV/Plugin.php +++ b/3rdparty/Sabre/CardDAV/Plugin.php @@ -52,6 +52,8 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin { $server->subscribeEvent('report', array($this,'report')); $server->subscribeEvent('onHTMLActionsPanel', array($this,'htmlActionsPanel')); $server->subscribeEvent('onBrowserPostAction', array($this,'browserPostAction')); + $server->subscribeEvent('beforeWriteContent', array($this, 'beforeWriteContent')); + $server->subscribeEvent('beforeCreateFile', array($this, 'beforeCreateFile')); /* Namespaces */ $server->xmlNamespaces[self::NS_CARDDAV] = 'card'; @@ -152,7 +154,7 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin { $val = stream_get_contents($val); // Taking out \r to not screw up the xml output - $returnedProperties[200][$addressDataProp] = str_replace("\r","", $val); + //$returnedProperties[200][$addressDataProp] = str_replace("\r","", $val); // The stripping of \r breaks the Mail App in OSX Mountain Lion // this is fixed in master, but not backported. /Tanghus $returnedProperties[200][$addressDataProp] = $val; @@ -286,6 +288,81 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin { } + /** + * This method is triggered before a file gets updated with new content. + * + * This plugin uses this method to ensure that Card nodes receive valid + * vcard data. + * + * @param string $path + * @param Sabre_DAV_IFile $node + * @param resource $data + * @return void + */ + public function beforeWriteContent($path, Sabre_DAV_IFile $node, &$data) { + + if (!$node instanceof Sabre_CardDAV_ICard) + return; + + $this->validateVCard($data); + + } + + /** + * This method is triggered before a new file is created. + * + * This plugin uses this method to ensure that Card nodes receive valid + * vcard data. + * + * @param string $path + * @param resource $data + * @param Sabre_DAV_ICollection $parentNode + * @return void + */ + public function beforeCreateFile($path, &$data, Sabre_DAV_ICollection $parentNode) { + + if (!$parentNode instanceof Sabre_CardDAV_IAddressBook) + return; + + $this->validateVCard($data); + + } + + /** + * Checks if the submitted iCalendar data is in fact, valid. + * + * An exception is thrown if it's not. + * + * @param resource|string $data + * @return void + */ + protected function validateVCard(&$data) { + + // If it's a stream, we convert it to a string first. + if (is_resource($data)) { + $data = stream_get_contents($data); + } + + // Converting the data to unicode, if needed. + $data = Sabre_DAV_StringUtil::ensureUTF8($data); + + try { + + $vobj = Sabre_VObject_Reader::read($data); + + } catch (Sabre_VObject_ParseException $e) { + + throw new Sabre_DAV_Exception_UnsupportedMediaType('This resource only supports valid vcard data. Parse error: ' . $e->getMessage()); + + } + + if ($vobj->name !== 'VCARD') { + throw new Sabre_DAV_Exception_UnsupportedMediaType('This collection can only support vcard objects.'); + } + + } + + /** * This function handles the addressbook-query REPORT * @@ -365,6 +442,8 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin { $vcard = Sabre_VObject_Reader::read($vcardData); + if (!$filters) return true; + foreach($filters as $filter) { $isDefined = isset($vcard->{$filter['name']}); diff --git a/3rdparty/Sabre/CardDAV/Property/SupportedAddressData.php b/3rdparty/Sabre/CardDAV/Property/SupportedAddressData.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/CardDAV/UserAddressBooks.php b/3rdparty/Sabre/CardDAV/UserAddressBooks.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/CardDAV/Version.php b/3rdparty/Sabre/CardDAV/Version.php old mode 100644 new mode 100755 index 811b929e39..d0623f0d3e --- a/3rdparty/Sabre/CardDAV/Version.php +++ b/3rdparty/Sabre/CardDAV/Version.php @@ -16,7 +16,7 @@ class Sabre_CardDAV_Version { /** * Full version number */ - const VERSION = '1.6.1'; + const VERSION = '1.6.3'; /** * Stability : alpha, beta, stable diff --git a/3rdparty/Sabre/CardDAV/includes.php b/3rdparty/Sabre/CardDAV/includes.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Auth/Backend/AbstractBasic.php b/3rdparty/Sabre/DAV/Auth/Backend/AbstractBasic.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Auth/Backend/AbstractDigest.php b/3rdparty/Sabre/DAV/Auth/Backend/AbstractDigest.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Auth/Backend/Apache.php b/3rdparty/Sabre/DAV/Auth/Backend/Apache.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Auth/Backend/File.php b/3rdparty/Sabre/DAV/Auth/Backend/File.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Auth/Backend/PDO.php b/3rdparty/Sabre/DAV/Auth/Backend/PDO.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Auth/IBackend.php b/3rdparty/Sabre/DAV/Auth/IBackend.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Auth/Plugin.php b/3rdparty/Sabre/DAV/Auth/Plugin.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Browser/GuessContentType.php b/3rdparty/Sabre/DAV/Browser/GuessContentType.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Browser/MapGetToPropFind.php b/3rdparty/Sabre/DAV/Browser/MapGetToPropFind.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Browser/Plugin.php b/3rdparty/Sabre/DAV/Browser/Plugin.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Browser/assets/favicon.ico b/3rdparty/Sabre/DAV/Browser/assets/favicon.ico old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Browser/assets/icons/addressbook.png b/3rdparty/Sabre/DAV/Browser/assets/icons/addressbook.png old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Browser/assets/icons/calendar.png b/3rdparty/Sabre/DAV/Browser/assets/icons/calendar.png old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Browser/assets/icons/card.png b/3rdparty/Sabre/DAV/Browser/assets/icons/card.png old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Browser/assets/icons/collection.png b/3rdparty/Sabre/DAV/Browser/assets/icons/collection.png old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Browser/assets/icons/file.png b/3rdparty/Sabre/DAV/Browser/assets/icons/file.png old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Browser/assets/icons/parent.png b/3rdparty/Sabre/DAV/Browser/assets/icons/parent.png old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Browser/assets/icons/principal.png b/3rdparty/Sabre/DAV/Browser/assets/icons/principal.png old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Client.php b/3rdparty/Sabre/DAV/Client.php old mode 100644 new mode 100755 index a8320dd978..9a428765e9 --- a/3rdparty/Sabre/DAV/Client.php +++ b/3rdparty/Sabre/DAV/Client.php @@ -11,7 +11,7 @@ * @package Sabre * @subpackage DAVClient * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. - * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_DAV_Client { @@ -23,6 +23,28 @@ class Sabre_DAV_Client { protected $password; protected $proxy; + /** + * Basic authentication + */ + const AUTH_BASIC = 1; + + /** + * Digest authentication + */ + const AUTH_DIGEST = 2; + + /** + * The authentication type we're using. + * + * This is a bitmask of AUTH_BASIC and AUTH_DIGEST. + * + * If DIGEST is used, the client makes 1 extra request per request, to get + * the authentication tokens. + * + * @var int + */ + protected $authType; + /** * Constructor * @@ -46,7 +68,7 @@ class Sabre_DAV_Client { 'baseUri', 'userName', 'password', - 'proxy' + 'proxy', ); foreach($validSettings as $validSetting) { @@ -55,6 +77,12 @@ class Sabre_DAV_Client { } } + if (isset($settings['authType'])) { + $this->authType = $settings['authType']; + } else { + $this->authType = self::AUTH_BASIC | self::AUTH_DIGEST; + } + $this->propertyMap['{DAV:}resourcetype'] = 'Sabre_DAV_Property_ResourceType'; } @@ -252,9 +280,6 @@ class Sabre_DAV_Client { ); switch ($method) { - case 'PUT': - $curlSettings[CURLOPT_PUT] = true; - break; case 'HEAD' : // do not read body with HEAD requests (this is neccessary because cURL does not ignore the body with HEAD @@ -285,8 +310,15 @@ class Sabre_DAV_Client { $curlSettings[CURLOPT_PROXY] = $this->proxy; } - if ($this->userName) { - $curlSettings[CURLOPT_HTTPAUTH] = CURLAUTH_BASIC | CURLAUTH_DIGEST; + if ($this->userName && $this->authType) { + $curlType = 0; + if ($this->authType & self::AUTH_BASIC) { + $curlType |= CURLAUTH_BASIC; + } + if ($this->authType & self::AUTH_DIGEST) { + $curlType |= CURLAUTH_DIGEST; + } + $curlSettings[CURLOPT_HTTPAUTH] = $curlType; $curlSettings[CURLOPT_USERPWD] = $this->userName . ':' . $this->password; } diff --git a/3rdparty/Sabre/DAV/Collection.php b/3rdparty/Sabre/DAV/Collection.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Directory.php b/3rdparty/Sabre/DAV/Directory.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Exception.php b/3rdparty/Sabre/DAV/Exception.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Exception/BadRequest.php b/3rdparty/Sabre/DAV/Exception/BadRequest.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Exception/Conflict.php b/3rdparty/Sabre/DAV/Exception/Conflict.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Exception/ConflictingLock.php b/3rdparty/Sabre/DAV/Exception/ConflictingLock.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Exception/FileNotFound.php b/3rdparty/Sabre/DAV/Exception/FileNotFound.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Exception/Forbidden.php b/3rdparty/Sabre/DAV/Exception/Forbidden.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Exception/InsufficientStorage.php b/3rdparty/Sabre/DAV/Exception/InsufficientStorage.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Exception/InvalidResourceType.php b/3rdparty/Sabre/DAV/Exception/InvalidResourceType.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Exception/LockTokenMatchesRequestUri.php b/3rdparty/Sabre/DAV/Exception/LockTokenMatchesRequestUri.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Exception/Locked.php b/3rdparty/Sabre/DAV/Exception/Locked.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Exception/MethodNotAllowed.php b/3rdparty/Sabre/DAV/Exception/MethodNotAllowed.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Exception/NotAuthenticated.php b/3rdparty/Sabre/DAV/Exception/NotAuthenticated.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Exception/NotFound.php b/3rdparty/Sabre/DAV/Exception/NotFound.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Exception/NotImplemented.php b/3rdparty/Sabre/DAV/Exception/NotImplemented.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Exception/PaymentRequired.php b/3rdparty/Sabre/DAV/Exception/PaymentRequired.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Exception/PreconditionFailed.php b/3rdparty/Sabre/DAV/Exception/PreconditionFailed.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Exception/ReportNotImplemented.php b/3rdparty/Sabre/DAV/Exception/ReportNotImplemented.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php b/3rdparty/Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Exception/UnsupportedMediaType.php b/3rdparty/Sabre/DAV/Exception/UnsupportedMediaType.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/FS/Directory.php b/3rdparty/Sabre/DAV/FS/Directory.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/FS/File.php b/3rdparty/Sabre/DAV/FS/File.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/FS/Node.php b/3rdparty/Sabre/DAV/FS/Node.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/FSExt/Directory.php b/3rdparty/Sabre/DAV/FSExt/Directory.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/FSExt/File.php b/3rdparty/Sabre/DAV/FSExt/File.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/FSExt/Node.php b/3rdparty/Sabre/DAV/FSExt/Node.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/File.php b/3rdparty/Sabre/DAV/File.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/ICollection.php b/3rdparty/Sabre/DAV/ICollection.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/IExtendedCollection.php b/3rdparty/Sabre/DAV/IExtendedCollection.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/IFile.php b/3rdparty/Sabre/DAV/IFile.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/INode.php b/3rdparty/Sabre/DAV/INode.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/IProperties.php b/3rdparty/Sabre/DAV/IProperties.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/IQuota.php b/3rdparty/Sabre/DAV/IQuota.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Locks/Backend/Abstract.php b/3rdparty/Sabre/DAV/Locks/Backend/Abstract.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Locks/Backend/FS.php b/3rdparty/Sabre/DAV/Locks/Backend/FS.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Locks/Backend/File.php b/3rdparty/Sabre/DAV/Locks/Backend/File.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Locks/Backend/PDO.php b/3rdparty/Sabre/DAV/Locks/Backend/PDO.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Locks/LockInfo.php b/3rdparty/Sabre/DAV/Locks/LockInfo.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Locks/Plugin.php b/3rdparty/Sabre/DAV/Locks/Plugin.php old mode 100644 new mode 100755 index fd956950b8..035b3a6386 --- a/3rdparty/Sabre/DAV/Locks/Plugin.php +++ b/3rdparty/Sabre/DAV/Locks/Plugin.php @@ -292,7 +292,10 @@ class Sabre_DAV_Locks_Plugin extends Sabre_DAV_ServerPlugin { $this->server->tree->getNodeForPath($uri); // We need to call the beforeWriteContent event for RFC3744 - $this->server->broadcastEvent('beforeWriteContent',array($uri)); + // Edit: looks like this is not used, and causing problems now. + // + // See Issue 222 + // $this->server->broadcastEvent('beforeWriteContent',array($uri)); } catch (Sabre_DAV_Exception_NotFound $e) { diff --git a/3rdparty/Sabre/DAV/Mount/Plugin.php b/3rdparty/Sabre/DAV/Mount/Plugin.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Node.php b/3rdparty/Sabre/DAV/Node.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/ObjectTree.php b/3rdparty/Sabre/DAV/ObjectTree.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Property.php b/3rdparty/Sabre/DAV/Property.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Property/GetLastModified.php b/3rdparty/Sabre/DAV/Property/GetLastModified.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Property/Href.php b/3rdparty/Sabre/DAV/Property/Href.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Property/HrefList.php b/3rdparty/Sabre/DAV/Property/HrefList.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Property/IHref.php b/3rdparty/Sabre/DAV/Property/IHref.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Property/LockDiscovery.php b/3rdparty/Sabre/DAV/Property/LockDiscovery.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Property/ResourceType.php b/3rdparty/Sabre/DAV/Property/ResourceType.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Property/Response.php b/3rdparty/Sabre/DAV/Property/Response.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Property/ResponseList.php b/3rdparty/Sabre/DAV/Property/ResponseList.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Property/SupportedLock.php b/3rdparty/Sabre/DAV/Property/SupportedLock.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Property/SupportedReportSet.php b/3rdparty/Sabre/DAV/Property/SupportedReportSet.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Server.php b/3rdparty/Sabre/DAV/Server.php old mode 100644 new mode 100755 index 50b190e8fa..0dfac8b0c7 --- a/3rdparty/Sabre/DAV/Server.php +++ b/3rdparty/Sabre/DAV/Server.php @@ -215,7 +215,7 @@ class Sabre_DAV_Server { $DOM->appendChild($error); $error->appendChild($DOM->createElement('s:exception',get_class($e))); - $error->appendChild($DOM->createElement('s:message',htmlentities($e->getMessage()))); + $error->appendChild($DOM->createElement('s:message',$e->getMessage())); if ($this->debugExceptions) { $error->appendChild($DOM->createElement('s:file',$e->getFile())); $error->appendChild($DOM->createElement('s:line',$e->getLine())); @@ -1784,7 +1784,14 @@ class Sabre_DAV_Server { $etag = $node->getETag(); if ($etag===$ifMatchItem) { $haveMatch = true; + } else { + // Evolution has a bug where it sometimes prepends the " + // with a \. This is our workaround. + if (str_replace('\\"','"', $ifMatchItem) === $etag) { + $haveMatch = true; + } } + } if (!$haveMatch) { throw new Sabre_DAV_Exception_PreconditionFailed('An If-Match header was specified, but none of the specified the ETags matched.','If-Match'); diff --git a/3rdparty/Sabre/DAV/ServerPlugin.php b/3rdparty/Sabre/DAV/ServerPlugin.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/SimpleCollection.php b/3rdparty/Sabre/DAV/SimpleCollection.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/SimpleDirectory.php b/3rdparty/Sabre/DAV/SimpleDirectory.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/SimpleFile.php b/3rdparty/Sabre/DAV/SimpleFile.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/StringUtil.php b/3rdparty/Sabre/DAV/StringUtil.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/TemporaryFileFilterPlugin.php b/3rdparty/Sabre/DAV/TemporaryFileFilterPlugin.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Tree.php b/3rdparty/Sabre/DAV/Tree.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Tree/Filesystem.php b/3rdparty/Sabre/DAV/Tree/Filesystem.php old mode 100644 new mode 100755 index 85a9ee317b..40580ae366 --- a/3rdparty/Sabre/DAV/Tree/Filesystem.php +++ b/3rdparty/Sabre/DAV/Tree/Filesystem.php @@ -42,9 +42,9 @@ class Sabre_DAV_Tree_Filesystem extends Sabre_DAV_Tree { $realPath = $this->getRealPath($path); if (!file_exists($realPath)) throw new Sabre_DAV_Exception_NotFound('File at location ' . $realPath . ' not found'); if (is_dir($realPath)) { - return new Sabre_DAV_FS_Directory($path); + return new Sabre_DAV_FS_Directory($realPath); } else { - return new Sabre_DAV_FS_File($path); + return new Sabre_DAV_FS_File($realPath); } } diff --git a/3rdparty/Sabre/DAV/URLUtil.php b/3rdparty/Sabre/DAV/URLUtil.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/UUIDUtil.php b/3rdparty/Sabre/DAV/UUIDUtil.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/Version.php b/3rdparty/Sabre/DAV/Version.php old mode 100644 new mode 100755 index 5e5d15e403..274646240a --- a/3rdparty/Sabre/DAV/Version.php +++ b/3rdparty/Sabre/DAV/Version.php @@ -14,7 +14,7 @@ class Sabre_DAV_Version { /** * Full version number */ - const VERSION = '1.6.2'; + const VERSION = '1.6.4'; /** * Stability : alpha, beta, stable diff --git a/3rdparty/Sabre/DAV/XMLUtil.php b/3rdparty/Sabre/DAV/XMLUtil.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAV/includes.php b/3rdparty/Sabre/DAV/includes.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAVACL/AbstractPrincipalCollection.php b/3rdparty/Sabre/DAVACL/AbstractPrincipalCollection.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAVACL/Exception/AceConflict.php b/3rdparty/Sabre/DAVACL/Exception/AceConflict.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAVACL/Exception/NeedPrivileges.php b/3rdparty/Sabre/DAVACL/Exception/NeedPrivileges.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAVACL/Exception/NoAbstract.php b/3rdparty/Sabre/DAVACL/Exception/NoAbstract.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAVACL/Exception/NotRecognizedPrincipal.php b/3rdparty/Sabre/DAVACL/Exception/NotRecognizedPrincipal.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAVACL/Exception/NotSupportedPrivilege.php b/3rdparty/Sabre/DAVACL/Exception/NotSupportedPrivilege.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAVACL/IACL.php b/3rdparty/Sabre/DAVACL/IACL.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAVACL/IPrincipal.php b/3rdparty/Sabre/DAVACL/IPrincipal.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAVACL/IPrincipalBackend.php b/3rdparty/Sabre/DAVACL/IPrincipalBackend.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAVACL/Plugin.php b/3rdparty/Sabre/DAVACL/Plugin.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAVACL/Principal.php b/3rdparty/Sabre/DAVACL/Principal.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAVACL/PrincipalBackend/PDO.php b/3rdparty/Sabre/DAVACL/PrincipalBackend/PDO.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAVACL/PrincipalCollection.php b/3rdparty/Sabre/DAVACL/PrincipalCollection.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAVACL/Property/Acl.php b/3rdparty/Sabre/DAVACL/Property/Acl.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAVACL/Property/AclRestrictions.php b/3rdparty/Sabre/DAVACL/Property/AclRestrictions.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAVACL/Property/CurrentUserPrivilegeSet.php b/3rdparty/Sabre/DAVACL/Property/CurrentUserPrivilegeSet.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAVACL/Property/Principal.php b/3rdparty/Sabre/DAVACL/Property/Principal.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAVACL/Property/SupportedPrivilegeSet.php b/3rdparty/Sabre/DAVACL/Property/SupportedPrivilegeSet.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAVACL/Version.php b/3rdparty/Sabre/DAVACL/Version.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/DAVACL/includes.php b/3rdparty/Sabre/DAVACL/includes.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/HTTP/AWSAuth.php b/3rdparty/Sabre/HTTP/AWSAuth.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/HTTP/AbstractAuth.php b/3rdparty/Sabre/HTTP/AbstractAuth.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/HTTP/BasicAuth.php b/3rdparty/Sabre/HTTP/BasicAuth.php old mode 100644 new mode 100755 index a747cc6a31..f90ed24f5d --- a/3rdparty/Sabre/HTTP/BasicAuth.php +++ b/3rdparty/Sabre/HTTP/BasicAuth.php @@ -46,7 +46,7 @@ class Sabre_HTTP_BasicAuth extends Sabre_HTTP_AbstractAuth { if (strpos(strtolower($auth),'basic')!==0) return false; - return explode(':', base64_decode(substr($auth, 6))); + return explode(':', base64_decode(substr($auth, 6)),2); } diff --git a/3rdparty/Sabre/HTTP/DigestAuth.php b/3rdparty/Sabre/HTTP/DigestAuth.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/HTTP/Request.php b/3rdparty/Sabre/HTTP/Request.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/HTTP/Response.php b/3rdparty/Sabre/HTTP/Response.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/HTTP/Util.php b/3rdparty/Sabre/HTTP/Util.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/HTTP/Version.php b/3rdparty/Sabre/HTTP/Version.php old mode 100644 new mode 100755 index 23dc7f8a7a..e6b4f7e535 --- a/3rdparty/Sabre/HTTP/Version.php +++ b/3rdparty/Sabre/HTTP/Version.php @@ -14,7 +14,7 @@ class Sabre_HTTP_Version { /** * Full version number */ - const VERSION = '1.6.2'; + const VERSION = '1.6.4'; /** * Stability : alpha, beta, stable diff --git a/3rdparty/Sabre/HTTP/includes.php b/3rdparty/Sabre/HTTP/includes.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/VObject/Component.php b/3rdparty/Sabre/VObject/Component.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/VObject/Component/VAlarm.php b/3rdparty/Sabre/VObject/Component/VAlarm.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/VObject/Component/VCalendar.php b/3rdparty/Sabre/VObject/Component/VCalendar.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/VObject/Component/VEvent.php b/3rdparty/Sabre/VObject/Component/VEvent.php old mode 100644 new mode 100755 index 4cc1e36d7d..d6b910874d --- a/3rdparty/Sabre/VObject/Component/VEvent.php +++ b/3rdparty/Sabre/VObject/Component/VEvent.php @@ -42,14 +42,15 @@ class Sabre_VObject_Component_VEvent extends Sabre_VObject_Component { $effectiveStart = $this->DTSTART->getDateTime(); if (isset($this->DTEND)) { + + // The DTEND property is considered non inclusive. So for a 3 day + // event in july, dtstart and dtend would have to be July 1st and + // July 4th respectively. + // + // See: + // http://tools.ietf.org/html/rfc5545#page-54 $effectiveEnd = $this->DTEND->getDateTime(); - // If this was an all-day event, we should just increase the - // end-date by 1. Otherwise the event will last until the second - // the date changed, by increasing this by 1 day the event lasts - // all of the last day as well. - if ($this->DTSTART->getDateType() == Sabre_VObject_Element_DateTime::DATE) { - $effectiveEnd->modify('+1 day'); - } + } elseif (isset($this->DURATION)) { $effectiveEnd = clone $effectiveStart; $effectiveEnd->add( Sabre_VObject_DateTimeParser::parseDuration($this->DURATION) ); diff --git a/3rdparty/Sabre/VObject/Component/VJournal.php b/3rdparty/Sabre/VObject/Component/VJournal.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/VObject/Component/VTodo.php b/3rdparty/Sabre/VObject/Component/VTodo.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/VObject/DateTimeParser.php b/3rdparty/Sabre/VObject/DateTimeParser.php old mode 100644 new mode 100755 index 1e2d54ef3a..23a4bb6991 --- a/3rdparty/Sabre/VObject/DateTimeParser.php +++ b/3rdparty/Sabre/VObject/DateTimeParser.php @@ -125,6 +125,9 @@ class Sabre_VObject_DateTimeParser { } + if ($duration==='P') { + $duration = 'PT0S'; + } $iv = new DateInterval($duration); if ($invert) $iv->invert = true; @@ -150,6 +153,7 @@ class Sabre_VObject_DateTimeParser { } $newDur = ($matches['plusminus']==='-'?'-':'+') . trim($newDur); + if ($newDur === '+') { $newDur = '+0 seconds'; }; return $newDur; } diff --git a/3rdparty/Sabre/VObject/Element.php b/3rdparty/Sabre/VObject/Element.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/VObject/Element/DateTime.php b/3rdparty/Sabre/VObject/Element/DateTime.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/VObject/Element/MultiDateTime.php b/3rdparty/Sabre/VObject/Element/MultiDateTime.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/VObject/ElementList.php b/3rdparty/Sabre/VObject/ElementList.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/VObject/FreeBusyGenerator.php b/3rdparty/Sabre/VObject/FreeBusyGenerator.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/VObject/Node.php b/3rdparty/Sabre/VObject/Node.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/VObject/Parameter.php b/3rdparty/Sabre/VObject/Parameter.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/VObject/ParseException.php b/3rdparty/Sabre/VObject/ParseException.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/VObject/Property.php b/3rdparty/Sabre/VObject/Property.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/VObject/Property/DateTime.php b/3rdparty/Sabre/VObject/Property/DateTime.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/VObject/Property/MultiDateTime.php b/3rdparty/Sabre/VObject/Property/MultiDateTime.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/VObject/Reader.php b/3rdparty/Sabre/VObject/Reader.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/VObject/RecurrenceIterator.php b/3rdparty/Sabre/VObject/RecurrenceIterator.php old mode 100644 new mode 100755 index 833aa091ab..740270dd8f --- a/3rdparty/Sabre/VObject/RecurrenceIterator.php +++ b/3rdparty/Sabre/VObject/RecurrenceIterator.php @@ -337,6 +337,8 @@ class Sabre_VObject_RecurrenceIterator implements Iterator { $this->endDate = clone $this->startDate; if (isset($this->baseEvent->DURATION)) { $this->endDate->add(Sabre_VObject_DateTimeParser::parse($this->baseEvent->DURATION->value)); + } elseif ($this->baseEvent->DTSTART->getDateType()===Sabre_VObject_Property_DateTime::DATE) { + $this->endDate->modify('+1 day'); } } $this->currentDate = clone $this->startDate; @@ -561,7 +563,7 @@ class Sabre_VObject_RecurrenceIterator implements Iterator { */ public function fastForward(DateTime $dt) { - while($this->valid() && $this->getDTEnd() < $dt) { + while($this->valid() && $this->getDTEnd() <= $dt) { $this->next(); } @@ -823,9 +825,40 @@ class Sabre_VObject_RecurrenceIterator implements Iterator { */ protected function nextYearly() { + $currentMonth = $this->currentDate->format('n'); + $currentYear = $this->currentDate->format('Y'); + $currentDayOfMonth = $this->currentDate->format('j'); + + // No sub-rules, so we just advance by year if (!$this->byMonth) { + + // Unless it was a leap day! + if ($currentMonth==2 && $currentDayOfMonth==29) { + + $counter = 0; + do { + $counter++; + // Here we increase the year count by the interval, until + // we hit a date that's also in a leap year. + // + // We could just find the next interval that's dividable by + // 4, but that would ignore the rule that there's no leap + // year every year that's dividable by a 100, but not by + // 400. (1800, 1900, 2100). So we just rely on the datetime + // functions instead. + $nextDate = clone $this->currentDate; + $nextDate->modify('+ ' . ($this->interval*$counter) . ' years'); + } while ($nextDate->format('n')!=2); + $this->currentDate = $nextDate; + + return; + + } + + // The easiest form $this->currentDate->modify('+' . $this->interval . ' years'); return; + } $currentMonth = $this->currentDate->format('n'); @@ -877,8 +910,8 @@ class Sabre_VObject_RecurrenceIterator implements Iterator { } else { - // no byDay or byMonthDay, so we can just loop through the - // months. + // These are the 'byMonth' rules, if there are no byDay or + // byMonthDay sub-rules. do { $currentMonth++; @@ -888,6 +921,7 @@ class Sabre_VObject_RecurrenceIterator implements Iterator { } } while (!in_array($currentMonth, $this->byMonth)); $this->currentDate->setDate($currentYear, $currentMonth, $currentDayOfMonth); + return; } diff --git a/3rdparty/Sabre/VObject/Version.php b/3rdparty/Sabre/VObject/Version.php old mode 100644 new mode 100755 index 00110febc0..9ee03d8711 --- a/3rdparty/Sabre/VObject/Version.php +++ b/3rdparty/Sabre/VObject/Version.php @@ -14,7 +14,7 @@ class Sabre_VObject_Version { /** * Full version number */ - const VERSION = '1.3.2'; + const VERSION = '1.3.4'; /** * Stability : alpha, beta, stable diff --git a/3rdparty/Sabre/VObject/WindowsTimezoneMap.php b/3rdparty/Sabre/VObject/WindowsTimezoneMap.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/VObject/includes.php b/3rdparty/Sabre/VObject/includes.php old mode 100644 new mode 100755 diff --git a/3rdparty/Sabre/autoload.php b/3rdparty/Sabre/autoload.php old mode 100644 new mode 100755 diff --git a/3rdparty/aws-sdk/README.md b/3rdparty/aws-sdk/README.md new file mode 100644 index 0000000000..7e55f76b3b --- /dev/null +++ b/3rdparty/aws-sdk/README.md @@ -0,0 +1,136 @@ +# AWS SDK for PHP + +The AWS SDK for PHP enables developers to build solutions for Amazon Simple Storage Service (Amazon S3), +Amazon Elastic Compute Cloud (Amazon EC2), Amazon SimpleDB, and more. With the AWS SDK for PHP, developers +can get started in minutes with a single, downloadable package. + +The SDK features: + +* **AWS PHP Libraries:** Build PHP applications on top of APIs that take the complexity out of coding directly + against a web service interface. The toolkit provides APIs that hide much of the lower-level implementation. +* **Code Samples:** Practical examples for how to use the toolkit to build applications. +* **Documentation:** Complete SDK reference documentation with samples demonstrating how to use the SDK. +* **PEAR package:** The ability to install the AWS SDK for PHP as a PEAR package. +* **SDK Compatibility Test:** Includes both an HTML-based and a CLI-based SDK Compatibility Test that you can + run on your server to determine whether or not your PHP environment meets the minimum requirements. + +For more information about the AWS SDK for PHP, including a complete list of supported services, see +[aws.amazon.com/sdkforphp](http://aws.amazon.com/sdkforphp). + + +## Signing up for Amazon Web Services + +Before you can begin, you must sign up for each service you want to use. + +To sign up for a service: + +* Go to the home page for the service. You can find a list of services on + [aws.amazon.com/products](http://aws.amazon.com/products). +* Click the Sign Up button on the top right corner of the page. If you don't already have an AWS account, you + are prompted to create one as part of the sign up process. +* Follow the on-screen instructions. +* AWS sends you a confirmation e-mail after the sign-up process is complete. At any time, you can view your + current account activity and manage your account by going to [aws.amazon.com](http://aws.amazon.com) and + clicking "Your Account". + + +## Source +The source tree for includes the following files and directories: + +* `_compatibility_test` -- Includes both an HTML-based and a CLI-based SDK Compatibility Test that you can + run on your server to determine whether or not your PHP environment meets the minimum requirements. +* `_docs` -- Informational documents, the contents of which should be fairly self-explanatory. +* `_samples` -- Code samples that you can run out of the box. +* `extensions` -- Extra code that can be used to enhance usage of the SDK, but isn't a service class or a + third-party library. +* `lib` -- Contains any third-party libraries that the SDK depends on. The licenses for these projects will + always be Apache 2.0-compatible. +* `services` -- Contains the service-specific classes that communicate with AWS. These classes are always + prefixed with `Amazon`. +* `utilities` -- Contains any utility-type methods that the SDK uses. Includes extensions to built-in PHP + classes, as well as new functionality that is entirely custom. These classes are always prefixed with `CF`. +* `README` -- The document you're reading right now. +* `config-sample.inc.php` -- A sample configuration file that should be filled out and renamed to `config.inc.php`. +* `sdk.class.php` -- The SDK loader that you would include in your projects. Contains the base functionality + that the rest of the SDK depends on. + + +## Minimum Requirements in a nutshell + +* You are at least an intermediate-level PHP developer and have a basic understanding of object-oriented PHP. +* You have a valid AWS account, and you've already signed up for the services you want to use. +* The PHP interpreter, version 5.2 or newer. PHP 5.2.17 or 5.3.x is highly recommended for use with the AWS SDK for PHP. +* The cURL PHP extension (compiled with the [OpenSSL](http://openssl.org) libraries for HTTPS support). +* The ability to read from and write to the file system via [file_get_contents()](http://php.net/file_get_contents) and [file_put_contents()](http://php.net/file_put_contents). + +If you're not sure whether your PHP environment meets these requirements, run the +[SDK Compatibility Test](http://github.com/amazonwebservices/aws-sdk-for-php/tree/master/_compatibility_test/) script +included in the SDK download. + + +## Installation + +### Via GitHub + +[Git](http://git-scm.com) is an extremely fast, efficient, distributed version control system ideal for the +collaborative development of software. [GitHub](http://github.com/amazonwebservices) is the best way to +collaborate with others. Fork, send pull requests and manage all your public and private git repositories. +We believe that GitHub is the ideal service for working collaboratively with the open source PHP community. + +Git is primarily a command-line tool. GitHub provides instructions for installing Git on +[Mac OS X](http://help.github.com/mac-git-installation/), [Windows](http://help.github.com/win-git-installation/), +and [Linux](http://help.github.com/linux-git-installation/). If you're unfamiliar with Git, there are a variety +of resources on the net that will help you learn more: + +* [Git Immersion](http://gitimmersion.com) is a guided tour that walks through the fundamentals of Git, inspired + by the premise that to know a thing is to do it. +* The [PeepCode screencast on Git](https://peepcode.com/products/git) ($12) will teach you how to install and + use Git. You'll learn how to create a repository, use branches, and work with remote repositories. +* [Git Reference](http://gitref.org) is meant to be a quick reference for learning and remembering the most + important and commonly used Git commands. +* [Git Ready](http://gitready.com) provides a collection of Git tips and tricks. +* If you want to dig even further, I've [bookmarked other Git references](http://pinboard.in/u:skyzyx/t:git). + +If you're comfortable working with Git and/or GitHub, you can pull down the source code as follows: + + git clone git://github.com/amazonwebservices/aws-sdk-for-php.git AWSSDKforPHP + cd ./AWSSDKforPHP + +### Via PEAR + +[PEAR](http://pear.php.net) stands for the _PHP Extension and Application Repository_ and is a framework and +distribution system for reusable PHP components. It is the PHP equivalent to package management software such as +[MacPorts](http://macports.org) and [Homebrew](https://github.com/mxcl/homebrew) for Mac OS X, +[Yum](http://fedoraproject.org/wiki/Tools/yum) and [Apt](http://wiki.debian.org/Apt) for GNU/Linux, +[RubyGems](http://rubygems.org) for Ruby, [Easy Install](http://packages.python.org/distribute/easy_install.html) +for Python, [Maven](http://maven.apache.org) for Java, and [NPM](http://npm.mape.me) for Node.js. + +PEAR packages are very easy to install, and are available in your PHP environment path so that they are accessible +to any PHP project. PEAR packages are not specific to your project, but rather to the machine that they're +installed on. + +From the command-line, you can install the SDK with PEAR as follows: + + pear channel-discover pear.amazonwebservices.com + pear install aws/sdk + +You may need to use `sudo` for the above commands. Once the SDK has been installed via PEAR, you can load it into +your project with: + + require_once 'AWSSDKforPHP/sdk.class.php'; + +### Configuration + +1. Copy the contents of [config-sample.inc.php](https://github.com/amazonwebservices/aws-sdk-for-php/raw/master/config-sample.inc.php) + and add your credentials as instructed in the file. +2. Move your file to `~/.aws/sdk/config.inc.php`. +3. Make sure that `getenv('HOME')` points to your user directory. If not you'll need to set + `putenv('HOME=')`. + + +## Additional Information + +* AWS SDK for PHP: +* Documentation: +* License: +* Discuss: diff --git a/3rdparty/aws-sdk/_compatibility_test/README.md b/3rdparty/aws-sdk/_compatibility_test/README.md new file mode 100644 index 0000000000..9e2f2d8940 --- /dev/null +++ b/3rdparty/aws-sdk/_compatibility_test/README.md @@ -0,0 +1,37 @@ +# Compatibility Test + +## Via your web browser + +1. Upload `sdk_compatibility_test.php` to the web-accessible root of your website. +For example, if your website is `www.example.com`, upload it so that you can get +to it at `www.example.com/sdk_compatibility_test.php` + +2. Open your web browser and go to the page you just uploaded. + + +## Via the command line + +### Windows + +1. Upload `sdk_compatibility_test_cli.php` to your server via SFTP. + +2. SSH/RDP into the machine, and find the directory where you uploaded the test. + +3. Run the test, and review the results: + + php .\sdk_compatibility_test_cli.php + + +### Non-Windows (Mac or *nix) + +1. Upload `sdk_compatibility_test_cli.php` to your server via SFTP. + +2. SSH into the machine, and find the directory where you uploaded the test. + +3. Set the executable bit: + + chmod +x ./sdk_compatibility_test_cli.php + +4. Run the test, and review the results: + + ./sdk_compatibility_test_cli.php diff --git a/3rdparty/aws-sdk/_compatibility_test/sdk_compatibility.inc.php b/3rdparty/aws-sdk/_compatibility_test/sdk_compatibility.inc.php new file mode 100644 index 0000000000..c17ec33d72 --- /dev/null +++ b/3rdparty/aws-sdk/_compatibility_test/sdk_compatibility.inc.php @@ -0,0 +1,75 @@ +=')); +$simplexml_ok = extension_loaded('simplexml'); +$dom_ok = extension_loaded('dom'); +$json_ok = (extension_loaded('json') && function_exists('json_encode') && function_exists('json_decode')); +$spl_ok = extension_loaded('spl'); +$pcre_ok = extension_loaded('pcre'); +$curl_ok = false; +if (function_exists('curl_version')) +{ + $curl_version = curl_version(); + $curl_ok = (function_exists('curl_exec') && in_array('https', $curl_version['protocols'], true)); +} +$file_ok = (function_exists('file_get_contents') && function_exists('file_put_contents')); + +// Optional, but recommended +$openssl_ok = (extension_loaded('openssl') && function_exists('openssl_sign')); +$zlib_ok = extension_loaded('zlib'); + +// Optional +$apc_ok = extension_loaded('apc'); +$xcache_ok = extension_loaded('xcache'); +$memcached_ok = extension_loaded('memcached'); +$memcache_ok = extension_loaded('memcache'); +$mc_ok = ($memcache_ok || $memcached_ok); +$pdo_ok = extension_loaded('pdo'); +$pdo_sqlite_ok = extension_loaded('pdo_sqlite'); +$sqlite2_ok = extension_loaded('sqlite'); +$sqlite3_ok = extension_loaded('sqlite3'); +$sqlite_ok = ($pdo_ok && $pdo_sqlite_ok && ($sqlite2_ok || $sqlite3_ok)); + +// Other +$int64_ok = (PHP_INT_MAX === 9223372036854775807); +$ini_memory_limit = get_ini('memory_limit'); +$ini_open_basedir = get_ini('open_basedir'); +$ini_safe_mode = get_ini('safe_mode'); +$ini_zend_enable_gc = get_ini('zend.enable_gc'); + +if ($php_ok && $int64_ok && $curl_ok && $simplexml_ok && $dom_ok && $spl_ok && $json_ok && $pcre_ok && $file_ok && $openssl_ok && $zlib_ok && ($apc_ok || $xcache_ok || $mc_ok || $sqlite_ok)) +{ + $compatiblity = REQUIREMENTS_ALL_MET; +} +elseif ($php_ok && $curl_ok && $simplexml_ok && $dom_ok && $spl_ok && $json_ok && $pcre_ok && $file_ok) +{ + $compatiblity = REQUIREMENTS_MIN_MET; +} +else +{ + $compatiblity = REQUIREMENTS_NOT_MET; +} + +function get_ini($config) +{ + $cfg_value = ini_get($config); + + if ($cfg_value === false || $cfg_value === '' || $cfg_value === 0) + { + return false; + } + elseif ($cfg_value === true || $cfg_value === '1' || $cfg_value === 1) + { + return true; + } +} + +function is_windows() +{ + return strtolower(substr(PHP_OS, 0, 3)) === 'win'; +} diff --git a/3rdparty/aws-sdk/_compatibility_test/sdk_compatibility_test.php b/3rdparty/aws-sdk/_compatibility_test/sdk_compatibility_test.php new file mode 100644 index 0000000000..b36a38ab35 --- /dev/null +++ b/3rdparty/aws-sdk/_compatibility_test/sdk_compatibility_test.php @@ -0,0 +1,789 @@ + + + + +AWS SDK for PHP: Environment Compatibility Test + + + + + + + + + + +
+
+ +
+

SDK Compatibility Test

+ +

Minimum Requirements

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TestShould BeWhat You Have
PHP5.2 or newer
cURL7.15.0 or newer, with SSL
SimpleXMLEnabled
DOMEnabled
SPLEnabled
JSONEnabled
PCREEnabled
File System Read/WriteEnabled
+ +

Optional Extensions

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TestWould Like To BeWhat You Have
OpenSSLEnabled
ZlibEnabled
APCEnabled
XCacheEnabled
MemcacheEnabled
MemcachedEnabled
PDOEnabled
PDO-SQLiteEnabled
SQLite 2Enabled
SQLite 3Enabled
+ +

Settings for php.ini

+ + + + + + + + + + + + + + + + + + + + + + + + + +
TestWould Like To BeWhat You Have
open_basediroff
safe_modeoff
zend.enable_gcon
+ +

Other

+ + + + + + + + + + + + + + + +
TestWould Like To BeWhat You Have
Architecture64-bit + (why?) +
+ +
+
+ + +
+

Bottom Line: Yes, you can!

+

Your PHP environment is ready to go, and can take advantage of all possible features!

+
+
+

What's Next?

+

You can download the latest version of the AWS SDK for PHP and install it by following the instructions. Also, check out our library of screencasts and tutorials.

+

Take the time to read "Getting Started" to make sure you're prepared to use the AWS SDK for PHP. No seriously, read it.

+
+ +
+

Bottom Line: Yes, you can!

+

Your PHP environment is ready to go! There are a couple of minor features that you won't be able to take advantage of, but nothing that's a show-stopper.

+
+
+

What's Next?

+

You can download the latest version of the AWS SDK for PHP and install it by following the instructions. Also, check out our library of screencasts and tutorials.

+

Take the time to read "Getting Started" to make sure you're prepared to use the AWS SDK for PHP. No seriously, read it.

+
+ +
+

Bottom Line: We're sorry…

+

Your PHP environment does not support the minimum requirements for the AWS SDK for PHP.

+
+
+

What's Next?

+

If you're using a shared hosting plan, it may be a good idea to contact your web host and ask them to install a more recent version of PHP and relevant extensions.

+

If you have control over your PHP environment, we recommended that you upgrade your PHP environment. Check out the "Set Up Your Environment" section of the Getting Started Guide for more information.

+
+ + + = REQUIREMENTS_MIN_MET): ?> +
+

Recommended settings for config.inc.php

+

Based on your particular server configuration, the following settings are recommended.

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
Configuration SettingRecommended Value
default_cache_configapcxcacheAny valid, server-writable file system path
certificate_authoritytrueLoading...
+
+
+ + +
+

Give me the details!

+ = REQUIREMENTS_MIN_MET): ?> +
    +
  1. Your environment meets the minimum requirements for using the AWS SDK for PHP!
  2. + + +
  3. You're still running PHP . The PHP 5.2 family is no longer supported by the PHP team, and future versions of the AWS SDK for PHP will require PHP 5.3 or newer.
  4. + + + +
  5. The OpenSSL extension is installed. This will allow you to use CloudFront Private URLs and decrypt Microsoft® Windows® instance passwords.
  6. + + + +
  7. The Zlib extension is installed. The SDK will request gzipped data whenever possible.
  8. + + + +
  9. You're running on a 32-bit system. This means that PHP does not correctly handle files larger than 2GB (this is a well-known PHP issue). For more information, please see: PHP filesize: Return values.
  10. + +
  11. Note that PHP on Microsoft® Windows® does not support 64-bit integers at all, even if both the hardware and PHP are 64-bit.
  12. + + + + +
  13. You have open_basedir or safe_mode enabled in your php.ini file. Sometimes PHP behaves strangely when these settings are enabled. Disable them if you can.
  14. + + + +
  15. The PHP garbage collector (available in PHP 5.3+) is not enabled in your php.ini file. Enabling zend.enable_gc will provide better memory management in the PHP core.
  16. + + + The file system'; } + if ($apc_ok) { $storage_types[] = 'APC'; } + if ($xcache_ok) { $storage_types[] = 'XCache'; } + if ($sqlite_ok && $sqlite3_ok) { $storage_types[] = 'SQLite 3'; } + elseif ($sqlite_ok && $sqlite2_ok) { $storage_types[] = 'SQLite 2'; } + if ($memcached_ok) { $storage_types[] = 'Memcached'; } + elseif ($memcache_ok) { $storage_types[] = 'Memcache'; } + ?> +
  17. Storage types available for response caching:
  18. +
+ + +

NOTE: You're missing the OpenSSL extension, which means that you won't be able to take advantage of CloudFront Private URLs or decrypt Microsoft® Windows® instance passwords. You're also missing the Zlib extension, which means that the SDK will be unable to request gzipped data from Amazon and you won't be able to take advantage of compression with the response caching feature.

+ +

NOTE: You're missing the Zlib extension, which means that the SDK will be unable to request gzipped data from Amazon and you won't be able to take advantage of compression with the response caching feature.

+ +

NOTE: You're missing the OpenSSL extension, which means that you won't be able to take advantage of CloudFront Private URLs or decrypt Microsoft® Windows® instance passwords.

+ + + +
    + +
  1. PHP: You are running an unsupported version of PHP.
  2. + + + +
  3. cURL: The cURL extension is not available. Without cURL, the SDK cannot connect to — or authenticate with — Amazon's services.
  4. + + + +
  5. SimpleXML: The SimpleXML extension is not available. Without SimpleXML, the SDK cannot parse the XML responses from Amazon's services.
  6. + + + +
  7. DOM: The DOM extension is not available. Without DOM, the SDK cannot transliterate JSON responses from Amazon's services into the common SimpleXML-based pattern used throughout the SDK.
  8. + + + +
  9. SPL: Standard PHP Library support is not available. Without SPL support, the SDK cannot autoload the required PHP classes.
  10. + + + +
  11. JSON: JSON support is not available. AWS leverages JSON heavily in many of its services.
  12. + + + +
  13. PCRE: Your PHP installation doesn't support Perl-Compatible Regular Expressions (PCRE). Without PCRE, the SDK cannot do any filtering via regular expressions.
  14. + + + +
  15. File System Read/Write: The file_get_contents() and/or file_put_contents() functions have been disabled. Without them, the SDK cannot read from, or write to, the file system.
  16. + +
+ +
+ +
+

NOTE: Passing this test does not guarantee that the AWS SDK for PHP will run on your web server — it only ensures that the requirements have been addressed.

+
+
+ +
+ + + + + + + diff --git a/3rdparty/aws-sdk/_compatibility_test/sdk_compatibility_test_cli.php b/3rdparty/aws-sdk/_compatibility_test/sdk_compatibility_test_cli.php new file mode 100755 index 0000000000..a6632d8978 --- /dev/null +++ b/3rdparty/aws-sdk/_compatibility_test/sdk_compatibility_test_cli.php @@ -0,0 +1,186 @@ +#! /usr/bin/env php += REQUIREMENTS_MIN_MET) +{ + echo success('Your environment meets the minimum requirements for using the AWS SDK for PHP!') . PHP_EOL . PHP_EOL; + if (version_compare(PHP_VERSION, '5.3.0') < 0) { echo '* You\'re still running PHP ' . PHP_VERSION . '. The PHP 5.2 family is no longer supported' . PHP_EOL . ' by the PHP team, and future versions of the AWS SDK for PHP will *require*' . PHP_EOL . ' PHP 5.3 or newer.' . PHP_EOL . PHP_EOL; } + if ($openssl_ok) { echo '* The OpenSSL extension is installed. This will allow you to use CloudFront' . PHP_EOL . ' Private URLs and decrypt Windows instance passwords.' . PHP_EOL . PHP_EOL; } + if ($zlib_ok) { echo '* The Zlib extension is installed. The SDK will request gzipped data' . PHP_EOL . ' whenever possible.' . PHP_EOL . PHP_EOL; } + if (!$int64_ok) { echo '* You\'re running on a 32-bit system. This means that PHP does not correctly' . PHP_EOL . ' handle files larger than 2GB (this is a well-known PHP issue).' . PHP_EOL . PHP_EOL; } + if (!$int64_ok && is_windows()) { echo '* Note that PHP on Microsoft(R) Windows(R) does not support 64-bit integers' . PHP_EOL . ' at all, even if both the hardware and PHP are 64-bit. http://j.mp/php64win' . PHP_EOL . PHP_EOL; } + + if ($ini_open_basedir || $ini_safe_mode) { echo '* You have open_basedir or safe_mode enabled in your php.ini file. Sometimes' . PHP_EOL . ' PHP behaves strangely when these settings are enabled. Disable them if you can.' . PHP_EOL . PHP_EOL; } + if (!$ini_zend_enable_gc) { echo '* The PHP garbage collector (available in PHP 5.3+) is not enabled in your' . PHP_EOL . ' php.ini file. Enabling zend.enable_gc will provide better memory management' . PHP_EOL . ' in the PHP core.' . PHP_EOL . PHP_EOL; } + + $storage_types = array(); + if ($file_ok) { $storage_types[] = 'The file system'; } + if ($apc_ok) { $storage_types[] = 'APC'; } + if ($xcache_ok) { $storage_types[] = 'XCache'; } + if ($sqlite_ok && $sqlite3_ok) { $storage_types[] = 'SQLite 3'; } + elseif ($sqlite_ok && $sqlite2_ok) { $storage_types[] = 'SQLite 2'; } + if ($memcached_ok) { $storage_types[] = 'Memcached'; } + elseif ($memcache_ok) { $storage_types[] = 'Memcache'; } + echo '* Storage types available for response caching:' . PHP_EOL . ' ' . implode(', ', $storage_types) . PHP_EOL . PHP_EOL; + + if (!$openssl_ok) { echo '* You\'re missing the OpenSSL extension, which means that you won\'t be able' . PHP_EOL . ' to take advantage of CloudFront Private URLs or Windows password decryption.' . PHP_EOL . PHP_EOL; } + if (!$zlib_ok) { echo '* You\'re missing the Zlib extension, which means that the SDK will be unable' . PHP_EOL . ' to request gzipped data from Amazon and you won\'t be able to take advantage' . PHP_EOL . ' of compression with the response caching feature.' . PHP_EOL . PHP_EOL; } +} +else +{ + if (!$php_ok) { echo '* ' . failure('PHP:') . ' You are running an unsupported version of PHP.' . PHP_EOL . PHP_EOL; } + if (!$curl_ok) { echo '* ' . failure('cURL:') . ' The cURL extension is not available. Without cURL, the SDK cannot' . PHP_EOL . ' connect to -- or authenticate with -- Amazon\'s services.' . PHP_EOL . PHP_EOL; } + if (!$simplexml_ok) { echo '* ' . failure('SimpleXML:') . ': The SimpleXML extension is not available. Without SimpleXML,' . PHP_EOL . ' the SDK cannot parse the XML responses from Amazon\'s services.' . PHP_EOL . PHP_EOL; } + if (!$dom_ok) { echo '* ' . failure('DOM:') . ': The DOM extension is not available. Without DOM, the SDK' . PHP_EOL . ' Without DOM, the SDK cannot transliterate JSON responses from Amazon\'s' . PHP_EOL . ' services into the common SimpleXML-based pattern used throughout the SDK.' . PHP_EOL . PHP_EOL; } + if (!$spl_ok) { echo '* ' . failure('SPL:') . ' Standard PHP Library support is not available. Without SPL support,' . PHP_EOL . ' the SDK cannot autoload the required PHP classes.' . PHP_EOL . PHP_EOL; } + if (!$json_ok) { echo '* ' . failure('JSON:') . ' JSON support is not available. AWS leverages JSON heavily in many' . PHP_EOL . ' of its services.' . PHP_EOL . PHP_EOL; } + if (!$pcre_ok) { echo '* ' . failure('PCRE:') . ' Your PHP installation doesn\'t support Perl-Compatible Regular' . PHP_EOL . ' Expressions (PCRE). Without PCRE, the SDK cannot do any filtering via' . PHP_EOL . ' regular expressions.' . PHP_EOL . PHP_EOL; } + if (!$file_ok) { echo '* ' . failure('File System Read/Write:') . ' The file_get_contents() and/or file_put_contents()' . PHP_EOL . ' functions have been disabled. Without them, the SDK cannot read from,' . PHP_EOL . ' or write to, the file system.' . PHP_EOL . PHP_EOL; } +} + +echo '----------------------------------------' . PHP_EOL; +echo PHP_EOL; + +if ($compatiblity === REQUIREMENTS_ALL_MET) +{ + echo success('Bottom Line: Yes, you can!') . PHP_EOL; + echo PHP_EOL; + echo 'Your PHP environment is ready to go, and can take advantage of all possible features!' . PHP_EOL; + + echo PHP_EOL; + echo info('Recommended settings for config.inc.php') . PHP_EOL; + echo PHP_EOL; + + echo "CFCredentials::set(array(" . PHP_EOL; + echo " '@default' => array(" . PHP_EOL; + echo " 'key' => 'aws-key'," . PHP_EOL; + echo " 'secret' => 'aws-secret'," . PHP_EOL; + echo " 'default_cache_config' => "; + if ($apc_ok) echo success('\'apc\''); + elseif ($xcache_ok) echo success('\'xcache\''); + elseif ($file_ok) echo success('\'/path/to/cache/folder\''); + echo "," . PHP_EOL; + echo " 'certificate_authority' => " . success($ssl_result ? 'true' : 'false') . PHP_EOL; + echo " )" . PHP_EOL; + echo "));" . PHP_EOL; +} +elseif ($compatiblity === REQUIREMENTS_MIN_MET) +{ + echo success('Bottom Line: Yes, you can!') . PHP_EOL; + echo PHP_EOL; + echo 'Your PHP environment is ready to go! There are a couple of minor features that' . PHP_EOL . 'you won\'t be able to take advantage of, but nothing that\'s a show-stopper.' . PHP_EOL; + + echo PHP_EOL; + echo info('Recommended settings for config.inc.php') . PHP_EOL; + echo PHP_EOL; + + echo "CFCredentials::set(array(" . PHP_EOL; + echo " '@default' => array(" . PHP_EOL; + echo " 'key' => 'aws-key'," . PHP_EOL; + echo " 'secret' => 'aws-secret'," . PHP_EOL; + echo " 'default_cache_config' => "; + if ($apc_ok) echo success('\'apc\''); + elseif ($xcache_ok) echo success('\'xcache\''); + elseif ($file_ok) echo success('\'/path/to/cache/folder\''); + echo "," . PHP_EOL; + echo " 'certificate_authority' => " . ($ssl_result ? 'false' : 'true') . PHP_EOL; + echo " )" . PHP_EOL; + echo "));" . PHP_EOL; +} +else +{ + echo failure('Bottom Line: We\'re sorry...') . PHP_EOL; + echo 'Your PHP environment does not support the minimum requirements for the ' . PHP_EOL . 'AWS SDK for PHP.' . PHP_EOL; +} + +echo PHP_EOL; diff --git a/3rdparty/aws-sdk/_docs/CHANGELOG.md b/3rdparty/aws-sdk/_docs/CHANGELOG.md new file mode 100644 index 0000000000..52db66f4f6 --- /dev/null +++ b/3rdparty/aws-sdk/_docs/CHANGELOG.md @@ -0,0 +1,1405 @@ +# Changelog: 1.5.6.2 "Gershwin" +Code name for Apple's never-released successor to the never-released Copeland. + +Launched Tuesday, May 29th, 2012. + +## Services +### AmazonDynamoDB +- **Fixed:** STS credentials were not always being cached correctly. + +---- + +# Changelog: 1.5.6.1 "Gershwin" +Code name for Apple's never-released successor to the never-released Copeland. + +Launched Tuesday, May 24th, 2012. + +## Services +### AmazonDynamoDB +- **Fixed:** STS credentials were not always being cached correctly. + +---- + +# Changelog: 1.5.6 "Gershwin" +Code name for Apple's never-released successor to the never-released Copeland. + +Launched Tuesday, May 15th, 2012. + +## Services +### AmazonSES +- **New:** Support for domain verification has been added to the SDK, which enables customers to verify an entire email domain. +- **New:** Requests to this service are now signed with Signature V4. + +---- + +# Changelog: 1.5.5 "Fishhead" +Code name for the Apple II File Mangement Utility. + +Launched Wednesday, May 9, 2012. + +## Services +### AmazonCloudFormation +* **New:** Requests to this service are now signed with Signature V4. + +### AmazonCloudFront +* **New:** Updated the supported API version to `2012-03-15`. + +### AmazonDynamoDB +* **New:** Support for the US West (Northern California), US West (Oregon), Asia Pacific "Southeast" (Signapore) endpoints have been added. + +### AmazonElasticBeanstalk +* **New:** Support for the new Asia Pacific "Northeast" (Japan) endpoint has been added. + +### AmazonStorageGateway +* **New:** Support for the AWS Storage Gateway service has been added to the SDK. + +--- + +# Changelog: 1.5.4 "Enterprise" +Code name for Mac OS X Server 1.0 (Rhapsody CR1). + +Launched Thursday, April 19, 2012. + +## Bug fixes and enhancements +* [PHP SDK Bug - Memory leak](https://forums.aws.amazon.com/thread.jspa?threadID=72310) +* [Does update_object work in 1.5.3?](https://forums.aws.amazon.com/thread.jspa?threadID=89297) +* [The value of CURLOPT_SSL_VERIFYHOST](https://forums.aws.amazon.com/thread.jspa?threadID=86186) +* [PHP SDK BUG: s3.class.php Line 2396 on 1.5.2](https://forums.aws.amazon.com/thread.jspa?threadID=86779) +* [first create_bucket(), then get_bucket_list()](https://forums.aws.amazon.com/thread.jspa?messageID=318885) +* [Issue with AmazonS3::get_object_list() max-keys](https://forums.aws.amazon.com/thread.jspa?threadID=85878) +* [Correct the "Bottom line" minimum requirements check](https://github.com/amazonwebservices/aws-sdk-for-php/pull/23) +* [S3 PHP SDK: copy_object() fails to update the header](http://stackoverflow.com/questions/7677837/s3-php-sdk-copy-object-fails-to-update-the-header) +* [Adds the following utility methods to simplexml.class.php](https://github.com/amazonwebservices/aws-sdk-for-php/pull/22) +* [Adding the ability to name a 'rule' for Object Expiration (suggested tweak)](https://forums.aws.amazon.com/thread.jspa?messageID=328023) + +## Runtime +* **New:** Support for Signature Version 4 has been added to the SDK. Signature Version 4 is now the default authentication method for AWS Identity and Access Management, AWS Security Token Service and Amazon CloudSearch. + +## Services +### AmazonCloudFront +* **New:** Support for a Minimum TTL of zero has been added to the SDK. + +### AmazonCloudSearch +* **New:** Support for Amazon CloudSearch has been added to the SDK. This includes only the Configuration API. + +### AmazonDynamoDB +* **New:** Support for BatchWriteItem API has been added to the SDK. +* **New:** Support for the European (Ireland) endpoint has been added. +* **New:** Support for the Asia Pacific "Northeast" (Tokyo) endpoint has been added. +* **New:** Amazon DynamoDB Session Handler has been added to the SDK. +* **New:** A simplified interface for adding attributes has been added to the SDK. + +### AmazonEC2 +* **New:** The new "m1.medium" instance type is now supported. +* **New:** Amazon EBS support for Volume Status and Volume Attributes have been added to the SDK. +* **New:** Amazon EBS support for Conversion Tasks has been added to the SDK. +* **New:** Amazon EC2 support for the Report Instance Status feature has been added to the SDK. +* **New:** Amazon VPC support for Network Interfaces has been added to the SDK. +* **Fixed:** Various parameter fixes have been applied. + +### AmazonIAM +* **New:** Support for Password Policies and the ability to change passwords has been added to the SDK. + +### AmazonS3 +* **New:** Support for pre-signed URLs using temporary credentials has been added to the SDK. +* **New:** Support for setting a custom name to Lifecycle (i.e., Object Expiration) rules has been added to the SDK. +* **New:** Support for pre-signed URLs with https has been added to the SDK. +* **Fixed:** Resolved an issue where setting a custom XML parsing class was not being respected. +* **Fixed:** Resolved an issue where the `get_object_list()` method would return an incorrect number of entries. +* **Fixed:** Resolved an issue where `update_object()` was attempting to COPY instead of REPLACE. +* **Fixed:** Resolved an issue stemming from using path-style URLs, `create_bucket()` + `list_bucket()` and the EU-West region. +* **Fixed:** Resolved an issue where XML responses were not being parsed consistently. +* **Fixed:** Resolved an issue where Private Streaming URLs contained a double-encoded signature. +* **Fixed:** The `Expect: 100-continue` HTTP header is now only sent during `create_object()` and `upload_part()` requests. + +## Utilities +### CFRuntime +* **Fixed:** Resolved an issue where `CURLOPT_SSL_VERIFYHOST` was not set strictly enough. +* **Fixed:** The `Expect: 100-continue` HTTP header is no longer set on every request. + +### CFSimpleXML +* **New:** Support for `matches()`, `starts_with()` and `ends_with()` methods have been added to the SDK. (Thanks [Wil Moore III](https://github.com/wilmoore)!) + +## Compatibility Test +* **New:** SDK Compatibility Test pages are marked up as to not be indexed by search engines. (Thanks [Eric Caron](http://www.ericcaron.com)!) +* **Fixed:** Duplicate code between the CLI and web versions of the SDK has been refactored. (Thanks [Jimmy Berry](https://github.com/boombatower)!) + +--- + +# Changelog: 1.5.3 "Darwin" +UNIX foundation upon which Mac OS X, Apple TV, and iOS are based. + +Launched Wednesday, Tuesday, February 21, 2012. + +## Bug fixes and enhancements +* [Fixing Issue with set_distribution_config](https://github.com/amazonwebservices/aws-sdk-for-php/pull/20) + +## Services +### AmazonCloudFront +* **Fixed:** Resolved an issue where the `set_distribution_config()` method could fail to satisfy an API constraint when using a custom origin server. (Thanks [zoxa](https://github.com/zoxa)!) + +### AmazonSWF +* **New:** Support for the new Amazon Simple Workflow Service has been added to the SDK. + +---- + +# Changelog: 1.5.2 "Copland" +Code name for Apple's never-released successor to System 7. + +Launched Wednesday, Febraury 1, 2012. + +## Bug fixes and enhancements +* [SSL Cert on PHP SDK 1.5.0.1 ](https://forums.aws.amazon.com/thread.jspa?threadID=84947) +* [Stream Wrapper need a buffer !](https://forums.aws.amazon.com/thread.jspa?threadID=85436) +* [Fixing Issue with set_distribution_config](https://github.com/amazonwebservices/aws-sdk-for-php/pull/20) +* [[Bug] SDK Autoloader Interferes with PHPExcel Autoloader](https://forums.aws.amazon.com/thread.jspa?threadID=85239) +* [get_object query does not always return the same content type](https://forums.aws.amazon.com/thread.jspa?threadID=84148) +* [AWSSDKforPHP/authentication/swift_transport_esmtp_signature_handler.class.p ](https://forums.aws.amazon.com/thread.jspa?threadID=85087) + +## Runtime +* **New:** Updated the CA Root Certificates file to version 1.81. +* **Fixed:** Resolved an issue in the autoloader where the matching logic was too aggressive in certain cases, causing subsequent autoloaders to never trigger. + +## Services +### AmazonAS +* **New:** Support for Auto Scaling Resource Tagging has been added to the SDK. + +### AmazonS3 +* **Fixed:** Resolved an issue where `delete_all_objects()` and `delete_all_object_versions()` was being limited to 1000 items. +* **Fixed:** Resolved an issue where `delete_bucket()` would fail to delete a bucket with the "force" option enabled if the bucket contained more than 1000 items. +* **Fixed:** Resolved an issue where JSON documents stored in Amazon S3 would be parsed into a native PHP object when retrieved. + +## Utilities +### S3StreamWrapper +* **New:** Support for multiple stream wrappers (e.g., one per region) has been added to the SDK. +* **Fixed:** Writes to Amazon S3 are now buffered, resolving issues with pushing more than 8k of data at a time. + +### CFJSON +* **Fixed:** The JSON-to-XML conversion code is now substantially more robust and better handles encoded characters. + +### CacheCore +* **Changed:** Formerly, attempting to cache to a file system location that didn't exist or was not writable by the PHP process would fail silently. This behavior has been changed to throw a `CacheFile_Exception`. + +---- + +# Changelog: 1.5.1 "Blue" +Code name for Macintosh System 7. + +Launched Wednesday, January 18, 2012. + +## Bug fixes and enhancements +* [Documentation patch](https://github.com/amazonwebservices/aws-sdk-for-php/pull/13) +* [Removed duplicate comment line.](https://github.com/amazonwebservices/aws-sdk-for-php/pull/17) +* [CFRuntime credentials handling issue](https://forums.aws.amazon.com/thread.jspa?messageID=310388) +* [PHP 5.2 bug in AWS SDK for PHP 1.5.x](https://forums.aws.amazon.com/thread.jspa?messageID=311543) +* [[Bug] Custom Curl Opts Lost During Retry](https://forums.aws.amazon.com/thread.jspa?threadID=84835) +* [json_last_error doesn't exist before php v 5.3.0](https://github.com/amazonwebservices/aws-sdk-for-php/pull/12) +* [XML still being parsed when use_cache_flow is false](https://github.com/amazonwebservices/aws-sdk-for-php/pull/15) +* [Bug ssl_verification option not respected for AmazonS3 ](https://forums.aws.amazon.com/thread.jspa?threadID=83710) +* [[Bug] Compatibility test for Garbage Collector enabled should use ini_get](https://forums.aws.amazon.com/thread.jspa?threadID=84156) + +## Runtime +* **Fixed:** Corrected an issue where calling `AmazonS3->get_object()` would continue to parse the content if caching was being leveraged. (Thanks [Eric Caron](http://www.ericcaron.com)!) +* **Fixed:** The autoloader now returns `false` for any class it doesn't match, allowing subsequent autoloaders to catch the class name. (Thanks [Eric Caron](http://www.ericcaron.com)!) +* **Fixed:** An issue that caused CloudWatch to fail to decompress gzipped data correctly has been resolved. +* **Fixed:** Resolved an issue with passing explicit credentials without requiring a config file or a `CFCredentials` declaration. +* **Fixed:** Resolved an issue which causes custom cURL options to be unset from the payload when retrying. + +## Services +### AmazonAS +* **New:** Support for Amazon SNS notifications and Tagging have been added to the SDK. + +### AmazonCloudFront +* **Fixed:** Resolved an issue with disabling SSL verification. +* **Fixed:** Resolved an issue where `AmazonCloudFront` were throwing warnings in `E_STRICT` mode. + +### AmazonCloudWatch +* **Fixed:** Resolved an issue with decompressing gzipped data. + +### AmazonDynamoDB +* **New:** Support for Amazon DynamoDB has been added to the SDK. +* **New:** Amazon DynamoDB requires a default cache configuration to be set in the credential set, otherwise it will not function properly. + +### AmazonS3 +* **Fixed:** Resolved an issue with disabling SSL verification. +* **Fixed:** Resolved multiple documentation issues. (Thanks [Aizat Faiz](http://aizatto.com) and [Jason Ardell](http://ardell.posterous.com/)!) +* **Fixed:** Resolved an issue where `AmazonS3` were throwing warnings in `E_STRICT` mode. + +### AmazonSNS +* **New:** Support for Short Messaging Service (SMS) endpoints has been added to the SDK. +* **New:** Support for Subscription Attributes has been added to the SDK. + +## Utilities +### CFJSON +* **Fixed:** Support for the handling of JSON nulls in PHP 5.2 has been improved. (Thanks [David Chan](http://www.chandeeland.org)!) + +## Compatibility Test +* **Fixed:** The SDK compatibility test now uses `ini_get()` instead of `get_cfg_var()` and `get_cfg_ini()` for more accurate test results. + + +---- + +# Changelog: 1.5 "Allegro" +Code name for Mac OS 8.5. + +Launched Wednesday, December 14, 2011 + +## Credentials +* !! BACKWARDS-INCOMPATIBLE CHANGE !! - The function signature of all service constructors has changed. Instead of passing a key and secret as the first and second parameters, the constructor now accepts a hash (associative array) containing `key` and `secret` keys. Please see the API reference documentation + +## Runtime +* !! BACKWARDS-INCOMPATIBLE CHANGE !! - The function signature of all service constructors has changed. Instead of passing a key and secret as the first and second parameters, the constructor now accepts a hash (associative array) containing `key` and `secret` keys. If you are explicitly passing a key and secret to the constructor, you will need to change your code. If you are simply inheriting your default credentials from a config file, you don't need to make any changes beyond upgrading your config file to the new 1.5 format. Please see the API reference documentation for more information. +* !! BACKWARDS-INCOMPATIBLE CHANGE !! - The method by which the `config.inc.php` file maintains its list of credentials has been re-factored and updated to support managing multiple sets of credentials in a single location (e.g., development, staging, production). +* !! BACKWARDS-INCOMPATIBLE CHANGE !! - The `init()` method has been renamed to `factory()` to better reflect what it actually does. +* !! BACKWARDS-INCOMPATIBLE CHANGE !! - The `adjust_offset()` method has been removed. Instead, please ensure that the machine's time is set correctly using an [NTP server](https://secure.wikimedia.org/wikipedia/en/wiki/Network_Time_Protocol). +* !! BACKWARDS-INCOMPATIBLE CHANGE !! - In version 1.4 we enabled a mode where -- for services that supported it -- a set of temporary credentials were fetched and cached before the first request. This functionality has been reverted. The use of short-term credentials must be explicitly enabled by instantiating the `AmazonSTS` class and passing those credentials into the service constructor. +* **New:** Improved the user directory lookup for the config file. +* **Changed:** Made `set_region()` an alias of `set_hostname()`. + +## Services +### AmazonAS +* **New:** Support for the South American (São Paulo) region has been added to the SDK. +* **New:** Plain english aliases have been added to the SDK: `REGION_VIRGINA`, `REGION_CALIFORNIA`, `REGION_OREGON`, `REGION_IRELAND`, `REGION_SINGAPORE`, `REGION_TOKYO`, `REGION_SAO_PAULO` + +### AmazonCloudFormation +* **New:** Support for the South American (São Paulo) region has been added to the SDK. +* **New:** Plain english aliases have been added to the SDK: `REGION_VIRGINA`, `REGION_CALIFORNIA`, `REGION_OREGON`, `REGION_IRELAND`, `REGION_SINGAPORE`, `REGION_TOKYO`, `REGION_SAO_PAULO` +* **New:** Support for cost estimation of CloudFormation templates has been added to the SDK. + +### AmazonCloudWatch +* **New:** Support for the South American (São Paulo) region has been added to the SDK. +* **New:** Plain english aliases have been added to the SDK: `REGION_VIRGINA`, `REGION_CALIFORNIA`, `REGION_OREGON`, `REGION_IRELAND`, `REGION_SINGAPORE`, `REGION_TOKYO`, `REGION_SAO_PAULO` + +### AmazonEC2 +* **New:** Support for the South American (São Paulo) region has been added to the SDK. +* **New:** Plain english aliases have been added to the SDK: `REGION_VIRGINA`, `REGION_CALIFORNIA`, `REGION_OREGON`, `REGION_IRELAND`, `REGION_SINGAPORE`, `REGION_TOKYO`, `REGION_SAO_PAULO` +* **New:** Support for 24x7 Reserved Instances has been added to the SDK. For more information, please see [New Amazon EC2 Reserved Instance Options Now Available](https://aws.amazon.com/about-aws/whats-new/2011/12/01/New-Amazon-EC2-Reserved-Instances-Options-Now-Available/). +* **New:** Support for VPC Spot Instances has been added to the SDK. For more information, please see [Announcing Amazon EC2 Spot Integration with Amazon VPC](https://aws.amazon.com/about-aws/whats-new/2011/10/11/announcing-amazon-ec2-spot-integration-with-amazon-vpc/). +* **New:** Support for VPC Everywhere has been added to the SDK. For more information, please see [Amazon VPC Generally Available in Multiple AZs in All Regions](https://aws.amazon.com/about-aws/whats-new/2011/08/03/Announcing-VPC-GA/). +* **New:** Instance Type-related constants have been added to the SDK: `INSTANCE_MICRO`, `INSTANCE_SMALL`, `INSTANCE_LARGE`, `INSTANCE_XLARGE`, `INSTANCE_HIGH_MEM_XLARGE`, `INSTANCE_HIGH_MEM_2XLARGE`, `INSTANCE_HIGH_MEM_4XLARGE`, `INSTANCE_HIGH_CPU_MEDIUM`, `INSTANCE_HIGH_CPU_XLARGE`, `INSTANCE_CLUSTER_4XLARGE`, `INSTANCE_CLUSTER_8XLARGE`, `INSTANCE_CLUSTER_GPU_XLARGE`. + +### AmazonElastiCache +* **New:** Support for US-West 1 (California), EU-West (Ireland), Asia Pacific Southeast (Singapore), and Asia Pacific Northeast (Tokyo) regions has been added to the SDK. For more information, please see [Amazon ElastiCache is now available in four additional AWS Regions and as a CloudFormation template](https://aws.amazon.com/about-aws/whats-new/2011/12/05/amazon-elasticache-new-regions/). +* **New:** Plain english aliases have been added to the SDK: `REGION_VIRGINA`, `REGION_CALIFORNIA`, `REGION_IRELAND`, `REGION_SINGAPORE`, `REGION_TOKYO` + +### AmazonElasticBeanstalk +* **New:** Plain english aliases have been added to the SDK: `REGION_VIRGINA` + +### AmazonELB +* **New:** Support for the South American (São Paulo) region has been added to the SDK. +* **New:** Plain english aliases have been added to the SDK: `REGION_VIRGINA`, `REGION_CALIFORNIA`, `REGION_OREGON`, `REGION_IRELAND`, `REGION_SINGAPORE`, `REGION_TOKYO`, `REGION_SAO_PAULO` +* **New:** Support for ELBs running in VPC has been added to the SDK. For more information, please see [Announcing Elastic Load Balancing in Amazon VPC](https://aws.amazon.com/about-aws/whats-new/2011/11/21/announcing-elastic-load-balancing-in-amazon-vpc/). + +### AmazonEMR +* **New:** Support for the South American (São Paulo) region has been added to the SDK. +* **New:** Plain english aliases have been added to the SDK: `REGION_VIRGINA`, `REGION_CALIFORNIA`, `REGION_OREGON`, `REGION_IRELAND`, `REGION_SINGAPORE`, `REGION_TOKYO`, `REGION_SAO_PAULO` +* **New:** Support for EMR AMI Versioning, new Hadoop and Pig versions, and EMR running in VPC has been added to the SDK. For more information, please see [Amazon Elastic MapReduce Announces Support for New Hadoop and Pig Versions, AMI Versioning, and Amazon VPC](https://aws.amazon.com/about-aws/whats-new/2011/12/11/amazon-elastic-mapreduce-ami-versioning-vpc/). + +### AmazonIAM +* **New:** Plain english aliases have been added to the SDK: `REGION_VIRGINA` + +### AmazonImportExport +* **New:** Plain english aliases have been added to the SDK: `REGION_VIRGINA` + +### AmazonRDS +* **New:** Support for the South American (São Paulo) region has been added to the SDK. +* **New:** Plain english aliases have been added to the SDK: `REGION_VIRGINA`, `REGION_CALIFORNIA`, `REGION_OREGON`, `REGION_IRELAND`, `REGION_SINGAPORE`, `REGION_TOKYO`, `REGION_SAO_PAULO` + +### AmazonS3 +* **New:** Support for the South American (São Paulo) region has been added to the SDK. +* **New:** Plain english aliases have been added to the SDK: `REGION_VIRGINA`, `REGION_CALIFORNIA`, `REGION_OREGON`, `REGION_IRELAND`, `REGION_SINGAPORE`, `REGION_TOKYO`, `REGION_SAO_PAULO` +* **New:** Support for an S3 Stream Wrapper has been added to the SDK. This enables users to read/write to Amazon S3 as though it were the local file system. +**Fixed:** The `get_object()` method no longer attempts to parse XML/JSON content. +**Fixed:** Simplified S3 region logic. Now uses fully-qualified domain names across the board. + +### AmazonSES +* **New:** Plain english aliases have been added to the SDK: `REGION_VIRGINA` + +### AmazonSDB +* **New:** Support for the South American (São Paulo) region has been added to the SDK. +* **New:** Plain english aliases have been added to the SDK: `REGION_VIRGINA`, `REGION_CALIFORNIA`, `REGION_OREGON`, `REGION_IRELAND`, `REGION_SINGAPORE`, `REGION_TOKYO`, `REGION_SAO_PAULO` + +### AmazonSNS +* **New:** Support for the South American (São Paulo) region has been added to the SDK. +* **New:** Plain english aliases have been added to the SDK: `REGION_VIRGINA`, `REGION_CALIFORNIA`, `REGION_OREGON`, `REGION_IRELAND`, `REGION_SINGAPORE`, `REGION_TOKYO`, `REGION_SAO_PAULO` + +### AmazonSQS +* **New:** Support for the South American (São Paulo) region has been added to the SDK. +* **New:** Plain english aliases have been added to the SDK: `REGION_VIRGINA`, `REGION_CALIFORNIA`, `REGION_OREGON`, `REGION_IRELAND`, `REGION_SINGAPORE`, `REGION_TOKYO`, `REGION_SAO_PAULO` + +### AmazonSTS +* **New:** Plain english aliases have been added to the SDK: `REGION_VIRGINA` + + +---- + +# Changelog: 1.4.8 "Zanarkand" + + +Launched Wednesday, December 7, 2011 + +## Services +### AmazonCloudFront +* **Fixed:** Merged in a pull request contributed by Ben Lumley: + +### AmazonEC2 +* **Fixed:** Resolved an issue where `set_region()` was not setting the correct endpoint for the region. + +### AmazonS3 +* **New:** Support for S3-side multi-object delete has been added to the SDK as the `delete_objects()` method. The implementations of `delete_all_objects()` and `delete_all_object_versions()` have been updated to use this new functionality. +* **Changed:** XML and JSON responses from `get_object()` are no longer parsed. The raw XML and JSON string content is now returned. + + +---- + +# Changelog: 1.4.7 "Yuna" + + +Launched Wednesday, November 9, 2011 + +## Service Classes +### AmazonAS +* **New:** Support for the US-West 2 (Oregon) region has been added to the SDK. + +### AmazonCloudFormation +* **New:** Support for the US-West 2 (Oregon) region has been added to the SDK. + +### AmazonCloudWatch +* **New:** Support for the US-West 2 (Oregon) region has been added to the SDK. +* **New:** Support for the US GovCloud region has been added to the SDK. + +### AmazonEC2 +* **New:** Support for the US-West 2 (Oregon) region has been added to the SDK. +* **New:** Support for the US GovCloud region has been added to the SDK. + +### AmazonELB +* **New:** Support for the US-West 2 (Oregon) region has been added to the SDK. + +### AmazonEMR +* **New:** Support for the US-West 2 (Oregon) region has been added to the SDK. + +### AmazonIAM +* **New:** Support for the US GovCloud region has been added to the SDK. + +### AmazonRDS +* **New:** Support for the US-West 2 (Oregon) region has been added to the SDK. + +### AmazonS3 +* **New:** Support for the US-West 2 (Oregon) region has been added to the SDK. +* **Fixed:** Resolved an issue where certain bits of metadata were not maintained during a copy operation. +* **Fixed:** Resolved an issue where an unsuccessful lookup of an existing content-type would throw a warning. +* **Fixed:** Resolved an issue where an exception would be thrown when a filesize lookup was attempted on an object that didn't exist. + +### AmazonSDB +* **New:** Support for the US-West 2 (Oregon) region has been added to the SDK. + +### AmazonSNS +* **New:** Support for the US-West 2 (Oregon) region has been added to the SDK. + +### AmazonSQS +* **New:** Support for the US-West 2 (Oregon) region has been added to the SDK. + + +---- + +# Changelog: 1.4.6 "Xezat" + + +Launched Thursday, November 3, 2011 + +## Service Classes +### AmazonIAM +* **New:** Support for a virtual MFA device. A virtual MFA device uses a software application that can generate six-digit authentication codes that are Open AuTHentication Time-based One-Time Password (OATHTOTP)-compatible. The software application can run on any mobile hardware device, including a smartphone. + + +---- + +# Changelog: 1.4.5 "Weiss" + + +Launched Friday, October 21, 2011 + +## Service Classes +### AmazonSQS +* **New:** Support for delayed queues and batch operations has been added to the SDK. + + +---- + +# Changelog: 1.4.4 "Vaan" + + +Launched Tuesday, October 12, 2011 + +## Runtime +* **Fixed:** Resolved an issue where a segmentation fault is triggererd when there are multiple autoloaders in the stack and one of them doesn't return a value. + +## Service Classes +### AmazonS3 +* **New:** Support for server-side encryption has been added to the SDK. + + +---- + +# Changelog: 1.4.3 "Ultros" + + +Launched Friday, September 30, 2011 + +## Service Classes +### AmazonCloudFormation +* **New:** Support for new features in CloudFormation have been added to the SDK. + +### AmazonS3 +* **Fixed:** Setting the default cache configuration no longer causes authentication errors in `AmazonS3`. + + +---- + +# Changelog: 1.4.2.1 "Tiamat, Part II" + + +Launched Wednesday, September 7, 2011 + +## Utility Classes +### RequestCore +* **Fixed:** RequestCore has updated the `cacert.pem` file from Mozilla. This update revokes trust from the DigiNotar and Staat der Nederlanden root certificates. + + +---- + +# Changelog: 1.4.2 "Tiamat" + + +Launched Thursday, September 1, 2011 + +## Service Classes +### AmazonEC2 +* **Fixed:** Requests made to Amazon EC2 now use the correct API version (2011-07-15). + +### AmazonELB +* **New:** A pre-defined set of ciphers may now be used for SSL termination at the Elastic Load Balancer. +* **New:** Application servers can now accept secure communication from the corresponding Elastic Load Balancer. +* **New:** In cases where HTTPS is required for all traffic entering the back-end server, Elastic Load Balancing can now perform health checks using HTTPS. +* **New:** White list of public keys can now be associated with back-end servers. Elastic Load Balancing authenticates back-end servers with the public keys in the white list and communicates only with back-end servers that pass this authentication check. + +## Utility Classes +### RequestCore +* **Fixed:** RequestCore has updated the `cacert.pem` file from Mozilla. This update revokes trust from the DigiNotar root certificate. + + +---- + +# Changelog: 1.4.1 "Sephiroth" + + +Launched Tuesday, August 23, 2011 + +## Service Classes +### AmazonElastiCache +* **New:** Support for Amazon ElastiCache has been added to the SDK. + +### AmazonEMR +* **New:** Support for Hadoop Bootstrap Actions has been added to the SDK. +* **New:** Support for Amazon Elastic MapReduce on Spot Instances has been added to the SDK. +* **New:** Support for Termination Protection has been added to the SDK. +* **Changed:** For the add_instance_groups() method, the $instance_groups and $job_flow_id parameters have been reversed. + +## Utility Classes +### CFHadoopBootstrap +* **New:** The `CFHadoopBootstrap` class has been added to the SDK. Simplifies the process of working with Hadoop system and daemon configurations in Amazon EMR. +* **New:** This class extends from the `CFHadoopBase` class. + + +---- + +# Changelog: 1.4 "Rikku" + + +Launched Wednesday, August 3, 2011 + +## Bug fixes and enhancements + +## Service Classes +### AmazonEC2 +* **New:** Support for Session-Based Authentication (SBA) leveraging Amazon Secure Token Service (STS) has been added to the SDK. + +### AmazonS3 +* **New:** Support for Session-Based Authentication (SBA) leveraging Amazon Secure Token Service (STS) has been added to the SDK. + +### AmazonSNS +* **New:** Support for Session-Based Authentication (SBA) leveraging Amazon Secure Token Service (STS) has been added to the SDK. + +### AmazonSQS +* **New:** Support for Session-Based Authentication (SBA) leveraging Amazon Secure Token Service (STS) has been added to the SDK. + +### AmazonSTS +* **New:** Support for the Amazon Secure Token Service (STS) has been added to the SDK. + +## Utility Classes +### CFRuntime +* **New:** The following anonymous datapoints are now collected in aggregate so that we can make more informed decisions about future SDK features: `memory_limit`, `date.timezone`, `open_basedir`, `safe_mode`, `zend.enable_gc`. + +## Compatibility Test +* **New:** Support for verifying the installed SSL certificate has been added to the compatibility test. +* **New:** Support for verifying the status of `open_basedir` and `safe_mode` has been added to the compatibility test. +* **New:** Support for verifying the status of the PHP 5.3 garbage collector has been added to the compatibility test. +* **New:** The compatibility test now recommends optimal values for the `AWS_CERTIFICATE_AUTHORITY` and `AWS_DEFAULT_CACHE_CONFIG` configuration options based on the system's configuration. + + +---- + +# Changelog: 1.3.7 "Quistis" + + +Launched Monday, July 25, 2011 + +## Bug fixes and enhancements +* Addressed minor bug fixes reported via the feedback form in the API Reference. + +## Service Classes +### AmazonAS +* **Changed:** Introduced backwards-incompatible changes to the put_scheduled_update_group_action() method. + + +---- + +# Changelog: 1.3.6 "Penelo" + + +Launched Tuesday, July 12, 2011 + +## Bug fixes and enhancements +* [[Bug Report] rawurlencode error when using SES and curlopts](https://forums.aws.amazon.com/thread.jspa?threadID=68484) + +## Service Classes +### AmazonCloudFormation +* **New:** Support for the `list_stacks()` method has been added to the SDK. + +### AmazonElasticBeanstalk +* **New:** Support for the `swap_environment_cnames()` method has been added to the SDK. + +### AmazonS3 +* **Fixed:** Additional information about maximum open connections has been added to the `create_mpu_object()` method. + +## Compatibility Test +* **New:** Now tests whether the system is 64- or 32-bit. + + +---- + +# Changelog: 1.3.5 "Occuria" + + +Launched Tuesday, June 21, 2011 + +## Service Classes +### AmazonS3 +* **New:** Support for S3 copy part has been added to the SDK. + + +---- + +# Changelog: 1.3.4 "Nero" + + +Launched Tuesday, June 7, 2011 + +## Bug fixes and enhancements +* [Bug in PHP SDK](https://forums.aws.amazon.com/thread.jspa?threadID=67502) +* [cURL error: SSL certificate problem (60) with aws-sdk-for-php 1.3.3](https://forums.aws.amazon.com/thread.jspa?threadID=68349) + + +## Service Classes +### AmazonEC2 +* **New:** Support for Local Availability Zone Pricing has been added to the SDK. + +### AmazonELB +* **New:** Elastic Load Balancing provides a special Amazon EC2 security group that you can use to ensure that a back-end Amazon EC2 instance receives traffic only from its load balancer. + +### AmazonRDS +* **New:** Support for Oracle databases has been added to the SDK. + + +## Utility Classes +### CFArray +* **New:** Added the init() method which simplifies the process of instantiating and chaining a class. +* **New:** Added support for associative arrays to `each()`, `map()` and `filter()`. + +### CFRequest +* **New:** Now supports the `AWS_CERTIFICATE_AUTHORITY` configuration option. + + +---- + +# Changelog: 1.3.3 "Moogle" + + +Launched Tuesday, May 10, 2011 + +## Bug fixes and enhancements +* [Bug in AmazonCloudFront::get_private_object_url](https://forums.aws.amazon.com/thread.jspa?threadID=64004) +* [SDK 1.3.2 - Call to undefined function json_last_error()](https://forums.aws.amazon.com/thread.jspa?threadID=64767) +* [CURLOPT_FOLLOWLOCATION cannot be activated when in safe_mode or an open_basedir](https://forums.aws.amazon.com/thread.jspa?threadID=61333) + + +## Service Classes +### AmazonCloudFront +* **Fixed:** Resolved an issue where the expires value for `get_private_object_url()` only accepted a string instead of a string or integer. + +### AmazonCloudWatch +* **New:** Support for CloudWatch custom user metrics has been added to the SDK. + + +## Extensions +### S3BrowserUpload +* **New:** Added the `S3BrowserUpload` class to the SDK. This class assists in generating the correct HTML/XHTML markup for uploading files to S3 via an HTML
element. + + +## Utility Classes +### CFArray +* **New:** Added the `init()` method which simplifies the process of instantiating and chaining a class. + +### CFHadoopBase +* **New:** The `CFHadoopBase` class has been extracted out of `CFHadoopStep` as a shared library. + +### CFHadoopStep +* **New:** The `CFHadoopBase` class has been extracted out of `CFHadoopStep` as a shared library. +* **New:** This class now extends from the `CFHadoopBase` class. + +### CFJSON +* **Fixed:** Resolved an issue where a PHP 5.3-specific function was being used. + +### CFPolicy +* **New:** Added the init() method which simplifies the process of instantiating and chaining a class. + +### CFSimpleXML +* **New:** Added the init() method which simplifies the process of instantiating and chaining a class. + +### RequestCore +* **Fixed:** Improvements to running in PHP environments with open_basedir enabled. +* **Fixed:** RequestCore now uses an up-to-date `cacert.pem` file from Mozilla instead of the Certificate Authority that libcurl or libopenssl was compiled with, which should resolve certain issues with making SSL connections to AWS services. + + +---- + +# Changelog: 1.3.2 "Luna" + + +Launched Tuesday, April 5, 2011 + +## New Features & Highlights (Summary) +* Support for Dedicated Instances within a Virtual Private Cloud on single-tenant hardware has been added to the SDK. +* Bug fixes and enhancements: + * [AmazonCloudWatch get_metric_statistics returns gzipped body](https://forums.aws.amazon.com/thread.jspa?threadID=62625) + + +## Service Classes +### AmazonCloudWatch +* **Fixed:** Worked around an issue where when CloudWatch sends back `Content-Encoding: gzip`, it really means `deflate`. When CloudWatch sends back `Content-Encoding: deflate`, it really means the data isn't encoded at all. + +### AmazonEC2 +* **New:** Support for Dedicated Instances within a Virtual Private Cloud on single-tenant hardware has been added to the SDK. + + +---- + +# Changelog: 1.3.1 "Kraken" + + +Launched Friday, March 25, 2011 + +## New Features & Highlights (Summary) +* Fixed issues with Signature v3 authentication (SES). +* Added gzip decoding. +* Added support for converting data to more alternate formats. +* Bug fixes and enhancements: + * [Cannot send email](https://forums.aws.amazon.com/thread.jspa?threadID=62833) + * [AmazonCloudWatch get_metric_statistics returns gzipped body](https://forums.aws.amazon.com/thread.jspa?threadID=62625) + + +## Utility Classes +### CFArray +* **New:** The `to_json()` and `to_yaml()` methoda have been added to the class. + +### CFGzipDecode +* **New:** Handles a variety of primary and edge cases around gzip/deflate decoding in PHP. + +### CFRuntime +* **New:** Gzip decoding has been added to the SDK. +* **Fixed:** The previous release contained a regression in the Signature v3 support that affected AmazonSES. This has been resolved. +* **Fixed:** Completed support for Signature v3 over HTTP connections. + +### CFSimpleXML +* **New:** The `to_stdClass()` and `to_yaml()` methoda have been added to the class. + + +---- + +# Changelog: 1.3 "Jecht" + + +Launched Tuesday, March 15, 2011 + +## New Features & Highlights (Summary) +* Support for VPC Internet Access has been added to the SDK. +* Bug fixes and enhancements: + * [AmazonEC2::register_image issue](https://forums.aws.amazon.com/thread.jspa?threadID=52499) + * [Automatic Parseing of XML objects](https://forums.aws.amazon.com/thread.jspa?threadID=61882) + +## Service Classes +### AmazonEC2 +* **New:** Support for VPC Internet Access has been added to the SDK. +* **Fixed:** The `$image_location` parameter in the `register_image()` method is no longer required. This is a backwards-incompatible change. + +### AmazonS3 +* **Fixed:** Resolved an issue in `get_object()` where using the `lastmodified` and `etag` parameters required both to be set before taking effect. They can now be set independently from each other. + + +## Utility classes +### CFArray +* **Changed:** The `reduce()` method has been renamed to `filter()`. `reduce()` is now simply an alias for `filter()`. + +### CFJSON +* **New:** Simplifies the task of normalizing XML and JSON responses as `CFSimpleXML` objects. + +### CFRuntime +* **New:** Preliminary support for Signature v3 over HTTP has been added to the SDK. This is useful for debugging Signature v3 issues over non-HTTPS connections. +* **Changed:** Classes that use the shared authentication method (i.e., NOT `AmazonS3` or `AmazonCloudFront`) will automatically convert JSON service responses into a `CFSimpleXML` object. +* **Changed:** Formerly, the SDK would attempt to sniff the content to determine the type. Now, the SDK will check the HTTP response headers for `text/xml`, `application/xml` or `application/json` to determine whether or not to parse the content. If the HTTP response headers are not available, the SDK will still attempt content sniffing. + +### CFSimpleXML +* **New:** The `to_json()` method has been added to the class. + +### CFUtilities +* **New:** The `is_json()` method has been added to the class. + + +---- + +# Changelog: 1.2.6 "Ifrit" + + +Launched Wednesday, March 2, 2011 + +## New Features & Highlights (Summary) +* **New:** Support for the new Asia Pacific "Northeast" (Japan) endpoint has been added for all relevant services. +* **New:** Support for registering callback functions for read/write streams has been added to the SDK. Includes a runnable sample. +* **Fixed:** Improvements to avoid triggering warnings when PHP is in Safe Mode. + + +## Service Classes +### AmazonAS +* **New:** Added a new _class_ constant: `REGION_APAC_NE1`. + +### AmazonCloudFormation +* **New:** Added a new _class_ constant: `REGION_APAC_NE1`. + +### AmazonCloudWatch +* **New:** Added a new _class_ constant: `REGION_APAC_NE1`. + +### AmazonEC2 +* **New:** Added a new _class_ constant: `REGION_APAC_NE1`. + +### AmazonELB +* **New:** Added a new _class_ constant: `REGION_APAC_NE1`. + +### AmazonRDS +* **New:** Added a new _class_ constant: `REGION_APAC_NE1`. + +### AmazonS3 +* **New:** Added a new _class_ constant: `REGION_APAC_NE1`. +* **New:** Added support for `ap-northeast-1` as a location constraint when creating a new bucket. + +### AmazonSDB +* **New:** Added a new _class_ constant: `REGION_APAC_NE1`. + +### AmazonSNS +* **New:** Added a new _class_ constant: `REGION_APAC_NE1`. + +### AmazonSQS +* **New:** Added a new _class_ constant: `REGION_APAC_NE1`. + +## Utility classes +### CFRuntime +* **New:** Support for registering callback functions for read/write streams has been added to the SDK. +* **New:** Future-proofed for future regional endpoints. + +### RequestCore +* **New:** Support for registering callback functions for read/write streams has been added to the SDK. +* **Fixed:** Improvements to avoid triggering warnings when PHP is in Safe Mode. + +## Samples +* **New:** A sample demonstrating how to add a command-line progress bar for S3 transfers has been added to the SDK. + + +---- + +# Changelog: 1.2.5 "Heidegger" + + +Launched Thursday, February 24, 2011 + +## New Features & Highlights (Summary) +* Support for AWS CloudFormation has been added to the SDK. +* Bug fixes and enhancements: + * [PHP API change_content_type() broken](https://forums.aws.amazon.com/thread.jspa?threadID=59532) + * [Bug setting OriginAccessIdentity for a Cloudfront distribution config](https://forums.aws.amazon.com/thread.jspa?threadID=60989) + +## Service Classes +### AmazonCloudFormation +* **New:** Support for AWS CloudFormation has been added to the SDK. + +### AmazonCloudFront +* **Fixed:** Issues around `update_xml_config()` have been resolved. + +### AmazonS3 +* **Fixed:** Issues around `change_content_type()` have been resolved. + + +---- + +# Changelog: 1.2.4 "Goltanna" + + +Launched Wednesday, February 16, 2011 + +## New Features & Highlights (Summary) +* Support for IAM account aliases and server certificates has been added to the SDK. +* Support for Amazon S3 Website Configuration has been added to the SDK. +* Documentation updates for Amazon RDS and AWS Import/Export. +* Updated all documentation blocks to adhere to the PHPDoc format. This enables a greater number of tools to take advantage of the SDK documentation. +* Rolled out a major update to the SDK API Reference. + +## Service Classes +### AmazonIAM +* **New:** Support for IAM account aliases and server certificates has been added to the SDK. + +### AmazonImportExport +* **New:** Documentation has been updated to note the new US West region support. + +### AmazonRDS +* **New:** Documentation has been updated to note the new support for MySQL 5.5. + +### AmazonS3 +* **New:** Support for Amazon S3 Website Configuration has been added to the SDK. + + +---- + +# Changelog: 1.2.3 "Fayth" + + +Launched Tuesday, January 25, 2010 + +## New Features & Highlights (Summary) +* Support for Amazon Simple Email Service has been added to the SDK. + +## Service Classes +### AmazonSES +* **New:** Support for Amazon Simple Email Service has been added to the SDK. + + +---- + +# Changelog: 1.2.2 "Esper" + + +Launched Tuesday, January 18, 2011 + +## New Features & Highlights (Summary) +* Support for Amazon Elastic Beanstalk has been added to the SDK. +* Bug fixes and enhancements: + * [AWS PHP S3 Library is not working out of the box](https://forums.aws.amazon.com/thread.jspa?threadID=55174) + * [Problem with create_mpu_object() and streaming_read_callback() in S3](https://forums.aws.amazon.com/thread.jspa?threadID=54541) + * [Integrated Uranium235's GitHub contributions](https://github.com/Uranium235/aws-sdk-for-php/compare/Streaming) + +## Service Classes +### AmazonElasticBeanstalk +* **New:** Support for AWS Elastic Beanstalk has been added to the SDK. + +### AmazonS3 +* **Fixed:** Major improvements to transferring data over streams. + +## Utility classes +###RequestCore +* **New:** Upgraded to version 1.4. +* **Fixed:** Major improvements to transferring data over streams. + + +---- + +# Changelog: 1.2.1 "Dio" + + +Launched Friday, January 14, 2011 + + +## New Features & Highlights (Summary) +* Support for S3 Response Headers has been added to the SDK. +* Bug fixes and enhancements: + * [copy_object failed between regions](https://forums.aws.amazon.com/thread.jspa?threadID=56893) + * [Possible S3 bug with multiple buckets?](https://forums.aws.amazon.com/thread.jspa?threadID=56561) + +## Service Classes +### AmazonS3 +* **New:** Support for S3 Response Headers has been added to the SDK. +* **New:** Documentation for Amazon S3 has been updated to include large object support details. +* **New:** The `abort_multipart_uploads_by_date()` method has been added to the SDK, which aborts multipart uploads that were initiated before a specific date. +* **Fixed:** Resolved an issue where the resource prefix wasn't being reset correctly. + +## Utility classes +### CFArray +* **New:** Instantiating the class without passing an array will use an empty array instead. +* **New:** Added the `compress()` method which removes null values from the array. +* **New:** Added the `reindex()` method which reindexes all array elements starting at zero. + +## Compatibility Test +* **New:** The command-line compatibility test now color-codes the responses. + + +---- + +# Changelog: 1.2 "Cloud" + + +Launched Friday, December 3, 2010 + + +## New Features & Highlights (Summary) +* Support for Amazon AutoScaling, Amazon Elastic MapReduce, and Amazon Import/Export Service has been added to the SDK. +* Support for metric alarms has been added to Amazon CloudWatch. +* Support for batch deletion has been added to Amazon SimpleDB. +* Bug fixes and enhancements: + * [EU Region DNS problem](https://forums.aws.amazon.com/thread.jspa?threadID=53028) + * [[SimpleDB] Conditional PUT](https://forums.aws.amazon.com/thread.jspa?threadID=55884) + * [Suggestions for the PHP SDK](https://forums.aws.amazon.com/thread.jspa?threadID=55210) + * [Updating a distribution config](https://forums.aws.amazon.com/thread.jspa?threadID=54888) + * [Problem with curlopt parameter in S3](https://forums.aws.amazon.com/thread.jspa?threadID=54532) + * [AmazonS3::get_object_list() doesn't consider max-keys option](https://forums.aws.amazon.com/thread.jspa?threadID=55169) + +## Base/Runtime class +* **New:** Added support for an alternate approach to instantiating classes which allows for method chaining (PHP 5.3+). +* **Changed:** Moved `CHANGELOG.md`, `CONTRIBUTORS.md`, `LICENSE.md` and `NOTICE.md` into a new `_docs` folder. +* **Changed:** Renamed the `samples` directory to `_samples`. +* **Changed:** Changed the permissions for the SDK files from `0755` to `0644`. +* **Fixed:** Resolved an issue where attempting to merge cURL options would fail. + +## Service Classes +### AmazonAS +* **New:** Support for the Amazon AutoScaling Service has been added to the SDK. + +### AmazonCloudFront +* **Fixed:** Resolved an issue where the incorrect formatting of an XML element prevented the ability to update the list of trusted signers. + +### AmazonCloudWatch +* **New:** Support for the Amazon CloudWatch `2010-08-01` service release expands Amazon's cloud monitoring offerings with custom alarms. +* **Changed:** The changes made to the `get_metric_statistics()` method are backwards-incompatible with the previous release. The `Namespace` and `Period` parameters are now required and the parameter order has changed. + +### AmazonEMR +* **New:** Support for the Amazon Elastic MapReduce Service has been added to the SDK. + +### AmazonImportExport +* **New:** Support for the Amazon Import/Export Service has been added to the SDK. + +### AmazonS3 +* **Fixed:** Resolved an issue in the `create_bucket()` method that caused the regional endpoint to be reset to US-Standard. +* **Fixed:** Resolved an issue in the `get_object_list()` method where the `max-keys` parameter was ignored. + +### AmazonSDB +* **New:** Support for `BatchDeleteAttributes` has been added to the SDK. +* **Fixed:** Resolved an issue where the `Expected` condition was not respected by `put_attributes()` or `delete_attributes()`. + + +## Utility classes +### CFComplexType +* **New:** You can now assign a `member` parameter to prefix all list identifiers. +* **Changed:** The `option_group()` method is now `public` instead of `private`. +* **Changed:** Rewrote the `to_query_string()` method to avoid the use of PHP's `http_build_query()` function because it uses `urlencode()` internally instead of `rawurlencode()`. + +### CFHadoopStep +* **New:** Simplifies the process of working with Hadoop steps in Amazon EMR. + +### CFManifest +* **New:** Simplifies the process of constructing YAML manifest documents for Amazon Import/Export Service. + +### CFStepConfig +* **New:** Simplifies the process of working with step configuration in Amazon EMR. + + +## Third-party Libraries +### CacheCore +* **Changed:** The `generate_timestamp()` method is now `protected` instead of `private`. + + +---- + +# Changelog: 1.1 "Barret" + + +Launched Wednesday, November 10, 2010 + + +## New Features & Highlights (Summary) +* Support for Amazon ELB, Amazon RDS and Amazon VPC has been added to the SDK. +* Support for the Amazon S3 multipart upload feature has been added to the SDK. This feature enables developers upload large objects in a series of requests for improved upload reliability. +* Support for the Amazon CloudFront custom origin (2010-11-01 release) feature has been added to the SDK. This feature enables developers to use custom domains as sources for Amazon CloudFront distributions. +* The `AmazonS3` class now supports reading from and writing to open file resources in addition to the already-supported file system paths. +* You can now seek to a specific byte-position within a file or file resource and begin streaming from that point when uploading or downloading objects. +* The methods `get_bucket_filesize()`, `get_object_list()`, `delete_all_objects()` and `delete_all_object_versions()` are no longer limited to 1000 entries and will work correctly for all entries. +* Requests that have errors at the cURL level now throw exceptions containing the error message and error code returned by cURL. +* Bug fixes and enhancements: + * [Bug in Samples](http://developer.amazonwebservices.com/connect/thread.jspa?threadID=52748) + * [EU Region DNS problem](http://developer.amazonwebservices.com/connect/thread.jspa?threadID=53028) + * [AmazonS3 get_bucket_object_count](http://developer.amazonwebservices.com/connect/thread.jspa?threadID=52976) + * [S3: get_object_list() fatal error](http://developer.amazonwebservices.com/connect/thread.jspa?threadID=53418) + * [S3 get_object_metadata() problems](http://developer.amazonwebservices.com/connect/thread.jspa?threadID=54244) + * [Bug in authenticate in sdk.class.php](http://developer.amazonwebservices.com/connect/thread.jspa?threadID=53117) + * [How to use Prefix with "get_object_list"?](http://developer.amazonwebservices.com/connect/thread.jspa?threadID=52987) + * [SignatureDoesNotMatch with utf-8 in SimpleDB](http://developer.amazonwebservices.com/connect/thread.jspa?threadID=52798) + * [Suggestion for the PHP SDK concerning streaming](http://developer.amazonwebservices.com/connect/thread.jspa?threadID=52787) + * [get_bucket_filesize only returns filesize for first 1000 objects](http://developer.amazonwebservices.com/connect/thread.jspa?threadID=53786) + + +## Base/Runtime class +* **Changed:** Port numbers other than 80 and 443 are now part of the signature. +* **Changed:** When putting UTF-8 characters via HTTP `POST`, a `SignatureDoesNotMatch` error would be returned. This was resolved by specifying the character set in the `Content-Type` header. + + +## Service Classes +### AmazonCloudFront +* **New:** Support for the Amazon CloudFront non-S3 origin feature (2010-11-01 release) has been added to the SDK. This feature enables developers to use non-S3 domains as sources for Amazon CloudFront distributions. + +### AmazonEC2 +* **New:** Support for Amazon Virtual Private Cloud has been added to the SDK. + +### AmazonELB +* **New:** Support for Amazon Elastic Load Balancing Service has been added to the SDK. + +### AmazonIAM +* **Fixed:** Removed `set_region()` as IAM only supports a single endpoint. + +### AmazonRDS +* **New:** Support for Amazon Relational Database Service has been added to the SDK. + +### AmazonS3 +* **New:** Support for the Amazon S3 multipart upload feature has been added to the SDK. This feature enables developers upload large objects in a series of requests for improved upload reliability. +* **New:** The `fileUpload` and `fileDownload` options now support reading from and writing to open file resources in addition to the already-supported file system paths. +* **Fixed:** In Amazon S3, requests directly to the eu-west endpoint must use the path-style URI. The set_region() method now takes this into account. +* **Fixed:** As of version 1.0.1, CFSimpleXML extends SimpleXMLIterator instead of SimpleXMLElement. This prevented the `__call()` magic method from firing when `get_object_list()` was used. +* **Fixed:** The `preauth` option for the `get_object_list()` method has been removed from the documentation as it is not supported. +* **Fixed:** The methods `get_bucket_filesize()`, `get_object_list()`, `delete_all_objects()` and `delete_all_object_versions()` are no longer limited to 1000 entries and will work correctly for all entries. +* **Fixed:** Using `delete_bucket()` to force-delete a bucket now works correctly for buckets with more than 1000 versions. +* **Fixed:** The response from the `get_object_metadata()` method now includes all supported HTTP headers, including metadata stored in `x-amz-meta-` headers. +* **Fixed:** Previously, if the `get_object_metadata()` method was called on a non-existant object, metadata for the alphabetically-next object would be returned. + +### AmazonSQS +* **New:** The `get_queue_arn()` method has been added to the `AmazonSQS` class, which converts a queue URI to a queue ARN. + + +## Utility classes +### CFSimpleXML +* **New:** Added `to_string()` and `to_array()` methods. + + +## Third-party Libraries +### RequestCore +* **New:** Upgraded to version 1.3. +* **New:** Added `set_seek_position()` for seeking to a byte-position in a file or file resource before starting an upload. +* **New:** Added support for reading from and writing to open file resources. +* **Fixed:** Improved the reporting for cURL errors. + + +## Compatibility Test +* **Fixed:** Fixed the links to the Getting Started Guide. + + +---- + +# Changelog: 1.0.1 "Aeris" + + +Launched Tuesday, October 12, 2010 + + +## New Features & Highlights (Summary) +* Improved support for running XPath queries against the service response bodies. +* Added support for request retries and exponential backoff. +* Added support for HTTP request/response header logging. +* Bug fixes and enhancements: + * [Bug in Samples](http://developer.amazonwebservices.com/connect/thread.jspa?threadID=52748) + * [Can't set ACL on object using the SDK](http://developer.amazonwebservices.com/connect/thread.jspa?threadID=52305) + * [Range requests for S3 - status codes 200, 206](http://developer.amazonwebservices.com/connect/thread.jspa?threadID=52738) + * [S3 change_storage_redundancy() function clears public-read ACL](http://developer.amazonwebservices.com/connect/thread.jspa?threadID=52652) + + +## Base/Runtime class +* **New:** Added support for request retries and exponential backoff for all `500` and `503` HTTP status codes. +* **New:** Added the `enable_debug_mode()` method to enable HTTP request/response header logging to `STDERR`. + + +## Service Classes +### AmazonS3 +* **Fixed:** Lots of tweaks to the documentation. +* **Fixed:** The `change_content_type()`, `change_storage_redundancy()`, `set_object_acl()`, and `update_object()` methods now respect the existing content-type, storage redundancy, and ACL settings when updating. +* **New:** Added the `get_object_metadata()` method has been added as a singular interface for obtaining all available metadata for an object. + + +## Utility Classes +### CFArray +* **New:** Added the `each()` method which accepts a callback function to execute for each entry in the array. Works similarly to [jQuery's each()](http://api.jquery.com/each). +* **New:** Added the `map()` method which accepts a callback function to execute for each entry in the array. Works similarly to [jQuery's map()](http://api.jquery.com/map). +* **New:** Added the `reduce()` method which accepts a callback function to execute for each entry in the array. Works similarly to [DomCrawler reduce()](http://github.com/symfony/symfony/blob/master/src/Symfony/Component/DomCrawler/Crawler.php) from the [Symfony 2](http://symfony-reloaded.org) Preview Release. +* **New:** Added the `first()` and `last()` methods to return the first and last nodes in the array, respectively. + +### CFInfo +* **New:** Retrieves information about the current installation of the AWS SDK for PHP. + +### CFSimpleXML +* **New:** Added the `query()` method, which allows for XPath queries while the results are wrapped in a `CFArray` response. +* **New:** Added the `parent()` method, which allows for traversing back up the document tree. +* **New:** Added the `stringify()` method, which typecasts the value as a string. +* **New:** Added the `is()` and `contains()` methods, which allow for testing whether the XML value is or contains a given value, respectively. +* **Changed:** Now extends the `SimpleXMLIterator` class, which in-turn extends the `SimpleXMLElement` class. This adds new iterator methods to the `CFSimpleXML` class. + + +## Third-party Libraries +### CacheCore +* **New:** Upgraded to version 1.2. +* **New:** Added a static `init` method that allows for chainable cache initialization (5.3+). + +### RequestCore +* **New:** Added `206` as a successful status code (i.e., Range GET). + + +## Compatibility Test +* **Fixed:** Some of the links in the compatibility test were missing. These have been fixed. + + +---- + +# Changelog: AWS SDK for PHP 1.0 + +Launched Tuesday, September 28, 2010 + +This is a complete list of changes since we forked from the CloudFusion 2.5.x trunk build. + + +## New Features & Highlights (Summary) +* The new file to include is `sdk.class.php` rather than `cloudfusion.class.php`. +* Because of the increased reliance on [JSON](http://json.org) across AWS services, the minimum supported version is now PHP 5.2 ([Released in November 2006](http://www.php.net/ChangeLog-5.php#5.2.0); Justified by these [WordPress usage statistics](http://wpdevel.wordpress.com/2010/07/09/suggest-topics-for-the-july-15-2010-dev/comment-page-1/#comment-8542) and the fact that [PHP 5.2 has been end-of-life'd](http://www.php.net/archive/2010.php#id2010-07-22-1) in favor of 5.3). +* Up-to-date service support for [EC2](http://aws.amazon.com/ec2), [S3](http://aws.amazon.com/s3), [SQS](http://aws.amazon.com/sqs), [SimpleDB](http://aws.amazon.com/simpledb), [CloudWatch](http://aws.amazon.com/cloudwatch), and [CloudFront](http://aws.amazon.com/cloudfront). +* Added service support for [SNS](http://aws.amazon.com/sns). +* Limited testing for third-party API-compatible services such as [Eucalyptus](http://open.eucalyptus.com), [Walrus](http://open.eucalyptus.com) and [Google Storage](http://sandbox.google.com/storage). +* Improved the consistency of setting complex data types across services. (Required some backwards-incompatible changes.) +* Added new APIs and syntactic sugar for SimpleXML responses, batch requests and response caching. +* Moved away from _global_ constants in favor of _class_ constants. +* Minor, but notable improvements to the monkey patching support. +* Added a complete list of bug fix and patch contributors. Give credit where credit is due. ;) + +**Note: ALL backwards-incompatible changes are noted below. Please review the changes if you are upgrading.** We're making a small number of backwards-incompatible changes in order to improve the consistency across services. We're making these changes _now_ so that we can ensure that future versions will always be backwards-compatible with the next major version change. + + +## File structure +The package file structure has been refined in a few ways: + +* All service-specific classes are inside the `/services/` directory. +* All utility-specific classes are inside the `/utilities/` directory. +* All third-party classes are inside the `/lib/` directory. + + +## Base/Runtime class +* **Fixed:** Resolved issues: [#206](http://code.google.com/p/tarzan-aws/issues/detail?id=206). +* **New:** The following global constants have been added: `CFRUNTIME_NAME`, `CFRUNTIME_VERSION`, `CFRUNTIME_BUILD`, `CFRUNTIME_URL`, and `CFRUNTIME_USERAGENT` +* **New:** Now supports camelCase versions of the snake_case method names. (e.g. `getObjectList()` will get translated to `get_object_list()` behind the scenes.) +* **New:** Added `set_resource_prefix()` and `allow_hostname_override()` (in addition to `set_hostname()`) to support third-party, API-compatible services. +* **New:** Added new caching APIs: `cache()` and `delete_cache()`, which work differently from the methods they replace. See docs for more information. +* **New:** Added new batch request APIs, `batch()` and `CFBatchRequest` which are intended to replace the old `returnCurlHandle` optional parameter. +* **New:** Will look for the `config.inc.php` file first in the same directory (`./config.inc.php`), and then fallback to `~/.aws/sdk/config.inc.php`. +* **Changed:** Renamed the `CloudFusion` base class to `CFRuntime`. +* **Changed:** `CloudFusion_Exception` has been renamed as `CFRuntime_Exception`. +* **Changed:** Renamed the `CloudFusion::$enable_ssl` property to `CFRuntime::$use_ssl`. +* **Changed:** Renamed the `CloudFusion::$set_proxy` property to `CFRuntime::$proxy`. +* **Changed:** `CFRuntime::disable_ssl()` no longer takes any parameters. Once SSL is off, it is always off for that class instance. +* **Changed:** All date-related constants are now class constants of the `CFUtilities` class (e.g. `CFUtilities::DATE_FORMAT_ISO8601`). + * Use `CFUtilities::konst()` if you're extending classes and need to do something such as `$this->util::DATE_FORMAT_ISO8601` but keep getting the `T_PAAMAYIM_NEKUDOTAYIMM` error. +* **Changed:** All `x-cloudfusion-` and `x-tarzan-` HTTP headers are now `x-aws-`. +* **Changed:** `CloudFusion::autoloader()` is now in its own separate class: `CFLoader::autoloader()`. This prevents it from being incorrectly inherited by extending classes. +* **Changed:** `RequestCore`, `ResponseCore` and `SimpleXMLElement` are now extended by `CFRequest`, `CFResponse` and `CFSimpleXML`, respectively. These new classes are now used by default. +* **Changed:** Changes to monkey patching: + * You must now extend `CFRequest` instead of `RequestCore`, and then pass that class name to `set_request_class()`. + * You must now extend `CFResponse` instead of `ResponseCore`, and then pass that class name to `set_response_class()`. + * You can now monkey patch `CFSimpleXML` (extended from `SimpleXMLElement`) with `set_parser_class()`. + * You can now monkey patch `CFBatchRequest` with `set_batch_class()`. + * No changes for monkey patching `CFUtilities` with `set_utilities_class()`. +* **Removed:** Removed ALL existing _global_ constants and replaced them with _class_ constants. +* **Removed:** Removed `cache_response()` and `delete_cache_response()`. + + +## Service classes + +### AmazonCloudFront +* **Fixed:** Resolved issues: [#124](http://code.google.com/p/tarzan-aws/issues/detail?id=124), [#225](http://code.google.com/p/tarzan-aws/issues/detail?id=225), [#229](http://code.google.com/p/tarzan-aws/issues/detail?id=229), [#232](http://code.google.com/p/tarzan-aws/issues/detail?id=232), [#239](http://code.google.com/p/tarzan-aws/issues/detail?id=239). +* **Fixed:** Fixed an issue where `AmazonCloudFront` sent a `RequestCore` user agent in requests. +* **New:** Class is now up-to-date with the [2010-07-15](http://docs.amazonwebservices.com/AmazonCloudFront/2010-07-15/APIReference/) API release. +* **New:** Added _class_ constants for deployment states: `STATE_INPROGRESS` and `STATE_DEPLOYED`. +* **New:** Now supports streaming distributions. +* **New:** Now supports HTTPS (as well as HTTPS-only) access. +* **New:** Now supports Origin Access Identities. Added `create_oai()`, `list_oais()`, `get_oai()`, `delete_oai()`, `generate_oai_xml()` and `update_oai_xml()`. +* **New:** Now supports private (signed) URLs. Added `get_private_object_url()`. +* **New:** Now supports default root objects. +* **New:** Now supports invalidation. +* **New:** Added `get_distribution_list()`, `get_streaming_distribution_list()` and `get_oai_list()` which return simplified arrays of identifiers. +* **Changed:** Replaced all of the remaining `CDN_*` constants with _class_ constants. + +### AmazonCloudWatch +* **New:** Added new _class_ constants: `DEFAULT_URL`, `REGION_US_E1`, `REGION_US_W1`, `REGION_EU_W1`, and `REGION_APAC_SE1`. +* **New:** Now supports the _Northern California_, _European_ and _Asia-Pacific_ regions. +* **New:** The _global_ `CW_DEFAULT_URL` constant has been replaced by `AmazonCloudFront::DEFAULT_URL`. + +### AmazonEC2 +* **Fixed:** Resolved issues: [#124](http://code.google.com/p/tarzan-aws/issues/detail?id=124), [#131](http://code.google.com/p/tarzan-aws/issues/detail?id=131), [#138](http://code.google.com/p/tarzan-aws/issues/detail?id=138), [#139](http://code.google.com/p/tarzan-aws/issues/detail?id=139), [#154](http://code.google.com/p/tarzan-aws/issues/detail?id=154), [#173](http://code.google.com/p/tarzan-aws/issues/detail?id=173), [#200](http://code.google.com/p/tarzan-aws/issues/detail?id=200), [#233](http://code.google.com/p/tarzan-aws/issues/detail?id=233). +* **New:** Class is now up-to-date with the [2010-06-15](http://docs.amazonwebservices.com/AWSEC2/2010-06-15/APIReference/) API release. +* **New:** Now supports [Paid AMIs](http://developer.amazonwebservices.com/connect/entry.jspa?externalID=865&categoryID=87). +* **New:** Now supports [Multiple instance types](http://developer.amazonwebservices.com/connect/entry.jspa?externalID=992&categoryID=87). +* **New:** Now supports [Elastic IPs](http://developer.amazonwebservices.com/connect/entry.jspa?externalID=1344&categoryID=87). +* **New:** Now supports [Availability Zones](http://developer.amazonwebservices.com/connect/entry.jspa?externalID=1344&categoryID=87). +* **New:** Now supports [Elastic Block Store](http://developer.amazonwebservices.com/connect/entry.jspa?externalID=1665&categoryID=87). +* **New:** Now supports [Windows instances](http://developer.amazonwebservices.com/connect/entry.jspa?externalID=1765&categoryID=87). +* **New:** Now supports the [European region](http://developer.amazonwebservices.com/connect/entry.jspa?externalID=1926&categoryID=87). +* **New:** Now supports the _Northern California_ and _Asia-Pacific_ regions. +* **New:** Now supports [Reserved instances](http://developer.amazonwebservices.com/connect/entry.jspa?externalID=2213&categoryID=87). +* **New:** Now supports [Shared snapshots](http://developer.amazonwebservices.com/connect/entry.jspa?externalID=2843&categoryID=87). +* **New:** Now supports [EBS AMIs](http://developer.amazonwebservices.com/connect/entry.jspa?externalID=3105&categoryID=87). +* **New:** Now supports [Spot instances](http://developer.amazonwebservices.com/connect/entry.jspa?externalID=3215&categoryID=87). +* **New:** Now supports [Cluster Compute Instances](http://developer.amazonwebservices.com/connect/entry.jspa?externalID=3965&categoryID=87). +* **New:** Now supports [Placement Groups](http://developer.amazonwebservices.com/connect/entry.jspa?externalID=3965&categoryID=87). +* **New:** Added new _class_ constants for regions: `REGION_US_E1`, `REGION_US_W1`, `REGION_EU_W1`, `REGION_APAC_SE1`. +* **New:** Added new _class_ constants for run-state codes: `STATE_PENDING`, `STATE_RUNNING`, `STATE_SHUTTING_DOWN`, `STATE_TERMINATED`, `STATE_STOPPING`, `STATE_STOPPED`. +* **New:** Added support for decrypting the Administrator password for Microsoft Windows instances. +* **New:** Instead of needing to pass `Parameter.0`, `Parameter.1`, ...`Parameter.n` individually to certain methods, you can now reliably pass a string for a single value or an indexed array for a list of values. +* **New:** Limited tested has been done with the Eucalyptus EC2-clone. +* **Changed:** The `$account_id` parameter has been removed from the constructor. +* **Changed:** The _global_ `EC2_LOCATION_US` and `EC2_LOCATION_EU` constants have been replaced. +* **Changed:** The `set_locale()` method has been renamed to `set_region()`. It accepts any of the region constants. + +### AmazonIAM +* **New:** Up-to-date with the [2010-03-31](http://docs.amazonwebservices.com/sns/2010-03-31/api/) API release. + +### AmazonS3 +* **Fixed:** Resolved issues: [#31](http://code.google.com/p/tarzan-aws/issues/detail?id=31), [#72](http://code.google.com/p/tarzan-aws/issues/detail?id=72), [#123](http://code.google.com/p/tarzan-aws/issues/detail?id=123), [#156](http://code.google.com/p/tarzan-aws/issues/detail?id=156), [#199](http://code.google.com/p/tarzan-aws/issues/detail?id=199), [#201](http://code.google.com/p/tarzan-aws/issues/detail?id=201), [#203](http://code.google.com/p/tarzan-aws/issues/detail?id=203), [#207](http://code.google.com/p/tarzan-aws/issues/detail?id=207), [#208](http://code.google.com/p/tarzan-aws/issues/detail?id=208), [#209](http://code.google.com/p/tarzan-aws/issues/detail?id=209), [#210](http://code.google.com/p/tarzan-aws/issues/detail?id=210), [#212](http://code.google.com/p/tarzan-aws/issues/detail?id=212), [#216](http://code.google.com/p/tarzan-aws/issues/detail?id=216), [#217](http://code.google.com/p/tarzan-aws/issues/detail?id=217), [#226](http://code.google.com/p/tarzan-aws/issues/detail?id=226), [#228](http://code.google.com/p/tarzan-aws/issues/detail?id=228), [#234](http://code.google.com/p/tarzan-aws/issues/detail?id=234), [#235](http://code.google.com/p/tarzan-aws/issues/detail?id=235). +* **Fixed:** Fixed an issue where `AmazonS3` sent a `RequestCore` user agent in requests. +* **New:** Now supports the _Northern California_ and _Asia-Pacific_ regions. +* **New:** Now supports the new _EU (Ireland)_ REST endpoint. +* **New:** Now supports MFA Delete. +* **New:** Now supports Conditional Copy. +* **New:** Now supports Reduced Redundancy Storage (RRS). Added `change_storage_redundancy()`. +* **New:** Now supports Object Versioning. Added `enable_versioning()`, `disable_versioning`, `get_versioning_status()`, and `list_bucket_object_versions()`. +* **New:** Now supports Bucket Policies. Added `set_bucket_policy()`, `get_bucket_policy()`, and `delete_bucket_policy()`. +* **New:** Now supports Bucket Notifications. Added `create_bucket_notification()`, `get_bucket_notifications()`, and `delete_bucket_notification()`. +* **New:** Added _class_ constants for regions: `REGION_US_E1`, `REGION_US_W1`, `REGION_EU_W1`, `REGION_APAC_SE1`. +* **New:** Added _class_ constants for storage types: `STORAGE_STANDARD` and `STORAGE_REDUCED`. +* **New:** Enhanced `create_object()` with the ability to upload a file from the file system. +* **New:** Enhanced `get_object()` with the ability to download a file to the file system. +* **New:** Enhanced `get_bucket_list()` and `get_object_list()` with performance improvements. +* **New:** Enhanced all GET operations with the ability to generate pre-authenticated URLs. This is the same feature as `get_object_url()` has had, applied to all GET operations. +* **New:** Limited testing with Walrus, the Eucalyptus S3-clone. +* **New:** Limited testing with Google Storage. +* **Changed:** Replaced all of the remaining `S3_*` constants with _class_ constants: `self::ACL_*`, `self::GRANT_*`, `self::USERS_*`, and `self::PCRE_ALL`. +* **Changed:** Changed the function signature for `create_object()`. The filename is now passed as the second parameter, while the remaining options are now passed as the third parameter. This behavior now matches all of the other object-related methods. +* **Changed:** Changed the function signature for `head_object()`, `delete_object()`, and `get_object_acl()`. The methods now accept optional parameters as the third parameter instead of simply `returnCurlHandle`. +* **Changed:** Changed the function signature for `get_object_url()` and `get_torrent_url()`. Instead of passing a number of seconds until the URL expires, you now pass a string that `strtotime()` understands (including `60 seconds`). +* **Changed:** Changed the function signature for `get_object_url()`. Instead of passing a boolean value for `$torrent`, the last parameter is now an `$opt` variable which allows you to set `torrent` and `method` parameters. +* **Changed:** Changed how `returnCurlHandle` is used. Instead of passing `true` as the last parameter to most methods, you now need to explicitly set `array('returnCurlHandle' => true)`. This behavior is consistent with the implementation in other classes. +* **Changed:** Optional parameter names changed in `list_objects()`: `maxKeys` is now `max-keys`. +* **Changed:** `get_bucket_locale()` is now called `get_bucket_region()`, and returns the response body as a _string_ for easier comparison with class constants. +* **Changed:** `get_bucket_size()` is now called `get_bucket_object_count()`. Everything else about it is identical. +* **Changed:** `head_bucket()` is now called `get_bucket_headers()`. Everything else about it is identical. +* **Changed:** `head_object()` is now called `get_object_headers()`. Everything else about it is identical. +* **Changed:** `create_bucket()` has two backward-incompatible changes: + * Method now **requires** the region (formerly _locale_) to be set. + * Method takes an `$acl` parameter so that the ACL can be set directly when creating a new bucket. +* **Changed:** Bucket names are now validated. Creating a new bucket now requires the more stringent DNS-valid guidelines, while the process of reading existing buckets follows the looser path-style guidelines. This change also means that the reading of path-style bucket names is now supported, when previously they weren’t. +* **Removed:** Removed `store_remote_file()` because its intended usage repeatedly confused users, and had potential for misuse. If you were using it to upload from the local file system, you should be using `create_object` instead. +* **Removed:** Removed `copy_bucket()`, `replace_bucket()`, `duplicate_object()`, `move_object()`, and `rename_object()` because only a small number of users used them, and they weren't very robust anyway. +* **Removed:** Removed `get_bucket()` because it was just an alias for `list_objects()` anyway. Use the latter from now on -- it's identical. + +### AmazonSDB +* **Fixed:** Resolved issues: [#205](http://code.google.com/p/tarzan-aws/issues/detail?id=205). +* **New:** Class is now up-to-date with the [2009-04-15](http://docs.amazonwebservices.com/AmazonSimpleDB/2009-04-15/DeveloperGuide/) API release. +* **Changed:** Changed the function signatures for `get_attributes()` and `delete_attributes()` to improve consistency. + +### AmazonSNS +* **New:** Up-to-date with the [2010-03-31](http://docs.amazonwebservices.com/sns/2010-03-31/api/) API release. + +### AmazonSQS +* **Fixed:** Resolved issues: [#137](http://code.google.com/p/tarzan-aws/issues/detail?id=137), [#213](http://code.google.com/p/tarzan-aws/issues/detail?id=213), [#219](http://code.google.com/p/tarzan-aws/issues/detail?id=219), [#220](http://code.google.com/p/tarzan-aws/issues/detail?id=220), [#221](http://code.google.com/p/tarzan-aws/issues/detail?id=221), [#222](http://code.google.com/p/tarzan-aws/issues/detail?id=222). +* **Fixed:** In CloudFusion 2.5, neither `add_permission()` nor `remove_permission()` were functional. They are now working. +* **New:** Now supports the _Northern California_ and _Asia-Pacific_ regions. +* **New:** Now supports the new _US-East (N. Virginia)_ endpoint. +* **New:** Now supports the new _EU (Ireland)_ endpoint. +* **New:** Added new _class_ constants for regions: `REGION_US_E1`, `REGION_US_W1`, `REGION_EU_W1`, and `REGION_APAC_SE1`. +* **Changed:** Because we now support multiple region endpoints, queue names alone are no longer sufficient for referencing your queues. As such, you must now use a full-queue URL instead of just the queue name. +* **Changed:** The _global_ `SQS_LOCATION_US` and `SQS_LOCATION_EU` constants have been replaced. +* **Changed:** Renamed `set_locale()` as `set_region()`. It accepts any of the region constants. +* **Changed:** Changed the function signature for `list_queues()`. See the updated API reference. +* **Changed:** Changed the function signature for `set_queue_attributes()`. See the updated API reference. +* **Changed:** Changed how `returnCurlHandle` is used. Instead of passing `true` as the last parameter to most methods, you now need to explicitly set `array('returnCurlHandle' => true)`. This behavior is consistent with the implementation in other classes. +* **Changed:** Function signature changed in `get_queue_attributes()`. The `$attribute_name` parameter is now passed as a value in the `$opt` parameter. + +### AmazonSQSQueue +* **Removed:** `AmazonSQSQueue` was a simple wrapper around the AmazonSDB class. It generally failed as an object-centric approach to working with SQS, and as such, has been eliminated. Use the `AmazonSQS` class instead. + + +## Utility Classes +### CFArray +* **New:** Extends `ArrayObject`. +* **New:** Simplified typecasting of SimpleXML nodes to native types (e.g. integers, strings). + +### CFBatchRequest +* **New:** Provides a higher-level API for executing batch requests. + +### CFComplexType +* **New:** Used internally by several classes to handle various complex data-types (e.g. single or multiple values, `Key.x.Subkey.y.Value` combinations). +* **New:** Introduces a way to convert between JSON, YAML, and the PHP equivalent of Lists and Maps (nested associative arrays). + +### CFRequest +* **New:** Sets some project-specific settings and passes them to the lower-level RequestCore. + +### CFResponse +* **New:** No additional changes from the base `ResponseCore` class. + +### CFPolicy +* **New:** Used for constructing Base64-encoded, JSON policy documents to be passed around to other methods. + +### CFSimpleXML +* **New:** Extends `SimpleXMLElement`. +* **New:** Simplified node retrieval. All SimpleXML-based objects (e.g. `$response->body`) now have magic methods that allow you to quickly retrieve nodes with the same name + * e.g. `$response->body->Name()` will return an array of all SimpleXML nodes that match the `//Name` XPath expression. + +### CFUtilities +* **Fixed:** `to_query_string()` now explicitly passes a `&` character to `http_build_query()` to avoid configuration issues with MAMP/WAMP/XAMP installations. +* **Fixed:** `convert_response_to_array()` has been fixed to correctly return an all-array response under both PHP 5.2 and 5.3. Previously, PHP 5.3 returned a mix of `array`s and `stdClass` objects. +* **New:** Added `konst()` to retrieve the value of a class constant, while avoiding the `T_PAAMAYIM_NEKUDOTAYIM` error. Misspelled because `const` is a reserved word. +* **New:** Added `is_base64()` to determine whether or not a string is Base64-encoded data. +* **New:** Added `decode_uhex()` to decode `\uXXXX` entities back into their unicode equivalents. +* **Changed:** Changed `size_readable()`. Now supports units up to exabytes. +* **Changed:** Moved the `DATE_FORMAT_*` _global_ constants into this class as _class_ constants. +* **Removed:** Removed `json_encode_php51()` now that the minimum required version is PHP 5.2 (which includes the JSON extension by default). +* **Removed:** Removed `hex_to_base64()`. + + +## Third-party Libraries +### CacheCore +* **New:** Upgraded to version 1.1.1. +* **New:** Now supports both the [memcache](http://php.net/memcache) extension, but also the newer, faster [memcached](http://php.net/memcached) extension. Prefers `memcached` if both are installed. +* **Deprecated:** Support for MySQL and PostgreSQL as storage mechanisms has been **deprecated**. Since they're using PDO, they'll continue to function (as we're maintaining SQLite support via PDO), but we recommend migrating to using APC, XCache, Memcache or SQLite if you'd like to continue using response caching. +* New BSD licensed +* + +### RequestCore +* **New:** Upgraded to version 1.2. +* **New:** Now supports streaming up and down. +* **New:** Now supports "rolling" requests for better scalability. +* New BSD licensed +* diff --git a/3rdparty/aws-sdk/_docs/CONTRIBUTORS.md b/3rdparty/aws-sdk/_docs/CONTRIBUTORS.md new file mode 100644 index 0000000000..3523bf6723 --- /dev/null +++ b/3rdparty/aws-sdk/_docs/CONTRIBUTORS.md @@ -0,0 +1,64 @@ +# Contributors + +## AWS SDK for PHP Contributors + +Contributions were provided under the Apache 2.0 License, as appropriate. + +The following people have provided ideas, support and bug fixes: + +* [arech8](http://developer.amazonwebservices.com/connect/profile.jspa?userID=154435) (bug fixes) +* [Aizat Faiz](http://aizatto.com) (bug fixes) +* [Ben Lumley](http://github.com/benlumley) (bug fixes) +* [David Chan](http://www.chandeeland.org) (bug fixes) +* [Eric Caron](http://www.ericcaron.com) (bug fixes) +* [Jason Ardell](http://ardell.posterous.com/) (bug fixes) +* [Jeremy Archuleta](http://code.google.com/u/jeremy.archuleta/) (bug fixes) +* [Jimmy Berry](http://blog.boombatower.com/) (bug fixes, patches) +* [Paul Voegler](mailto:voegler@gmx.de) (bug fixes, bug reports, patches) +* [Peter Bowen](http://github.com/pzb) (feedback, bug reports) +* [zoxa](https://github.com/zoxa) (bug fixes) + + +## CloudFusion/CacheCore/RequestCore Contributors + +Contributions were provided under the New BSD License, as appropriate. + +The following people have provided ideas, support and bug fixes: + +* [Aaron Collegeman](http://blog.aaroncollegeman.com) (bug fixes) +* [Alex Schenkel](http://code.google.com/u/alex.schenkel/) (bug fixes) +* [Andrzej Bednarczyk](http://kreo-consulting.com) (bug fixes) +* [bprater](http://code.google.com/u/bprater/) (bug fixes) +* [castoware](http://code.google.com/u/castoware/) (bug fixes) +* [Chris Chen](http://github.com/chrischen) (bug fixes, patches, support) +* [Chris Mytton](http://hecticjeff.net) (bug fixes) +* [evgen.dm](http://code.google.com/u/evgen.dm/) (bug fixes) +* [gafitescu](http://code.google.com/u/gafitescu/) (bug fixes) +* [Gary Richardson](http://code.google.com/u/gary.richardson/) (bug fixes) +* [Gil Hildebrand](http://squidoo.com) (bug fixes) +* [Guilherme Blanco](http://blog.bisna.com) (bug fixes) +* [hammjazz](http://code.google.com/u/hammjazz/) (bug fixes) +* [HelloBunty](http://code.google.com/u/HelloBunty/) (bug fixes) +* [inputrequired](http://code.google.com/u/inputrequired/) (bug fixes) +* [Ivo Beckers](http://infopractica.nl) (bug fixes) +* [Jason Litka](http://jasonlitka.com) (bug fixes, patches) +* [Jeremy Archuleta](http://code.google.com/u/jeremy.archuleta/) (bug fixes) +* [John Beales](http://johnbeales.com) (bug fixes) +* [John Parker](http://code.google.com/u/john3parker/) (bug fixes) +* [Jon Cianciullo](http://code.google.com/u/jon.cianciullo/) (bug fixes) +* [kris0476](http://code.google.com/u/kris0476/) (bug fixes) +* [Matt Terenzio](http://jour.nali.st/blog) (bug fixes, patches) +* [Mike Jetter](http://mbjetter.com) (bug fixes) +* [Morten Blinksbjerg Nielsen](http://mbn.dk) (bug fixes) +* [nathell](http://code.google.com/u/nathell/) (bug fixes) +* [nickgsuperstar](http://code.google.com/u/nickgsuperstar/) (bug fixes) +* [ofpichon](http://code.google.com/u/ofpichon/) (bug fixes) +* [Otavio Ferreira](http://otaviofff.me) (bug fixes) +* [Paul Voegler](mailto:voegler@gmx.de) (bug fixes, bug reports, patches) +* [Steve Brozosky](http://code.google.com/u/@UBZWSlJVBxhHXAN1/) (bug fixes) +* [Steve Chu](http://stevechu.org) (bug fixes) +* [tommusic](http://code.google.com/u/tommusic/) (bug fixes) +* [Tyler Hall](http://clickontyler.com) (bug fixes) +* [webcentrica.co.uk](http://code.google.com/u/@VhBQQldUBBBEXAF1/) (bug fixes) +* [worden341](http://github.com/worden341) (bug fixes) +* [yakkyjunk](http://code.google.com/u/yakkyjunk/) (bug fixes) diff --git a/3rdparty/aws-sdk/_docs/DYNAMODBSESSIONHANDLER.html b/3rdparty/aws-sdk/_docs/DYNAMODBSESSIONHANDLER.html new file mode 100644 index 0000000000..8b1545db50 --- /dev/null +++ b/3rdparty/aws-sdk/_docs/DYNAMODBSESSIONHANDLER.html @@ -0,0 +1,235 @@ + + + + + README + + + +

DynamoDB Session Handler

+

The DynamoDB Session Handler is a custom session handler for PHP that allows Amazon DynamoDB to be used as a session store while still using PHP’s native session functions.

+ +

What issues does this address?

+

The native PHP session handler stores sessions on the local file system. This is unreliable in distributed web applications, because the user may be routed to servers that do not contain the session data. To solve this problem, PHP developers have implemented custom solutions for storing user session data using databases, shared file systems, Memcache servers, tamper-proof cookies, etc., by taking advantage of the interface provided by PHP via the session_set_save_handler() function. Unfortunately, most of these solutions require a lot of configuration or prior knowledge about the storage mechanism in order to setup. Some of them also require the provisioning or management of additional servers.

+ +

Proposed solution

+

The DynamoDB Session Handler uses Amazon DynamoDB as a session store, which alleviates many of the problems with existing solutions. There are no additional servers to manage, and there is very little configuration required. The Amazon DynamoDB is also fundamentally designed for low latency (the data is even stored on SSDs), so the performance impact is much smaller when compared to other databases. Since the Session Handler is designed to be a drop in replacement for the default PHP session handler, it also implements session locking in a familiar way.

+ +

How do I use it?

+

The first step is to instantiate the Amazon DynamoDB client and register the session handler.

+
require_once 'AWSSDKforPHP/sdk.class.php';
+
+// Instantiate the Amazon DynamoDB client.
+// REMEMBER: You need to set 'default_cache_config' in your config.inc.php.
+$dynamodb = new AmazonDynamoDB();
+
+// Register the DynamoDB Session Handler.
+$handler = $dynamodb->register_session_handler(array(
+    'table_name' => 'my-sessions-table'
+));
+

Before you can use the session handler, you need to create a table to store the sessions in. This can be done through the AWS Console for Amazon DynamoDB, or using the session handler class (which you must configure with the table name like in the example above).

+
// Create a table for session storage with default settings.
+$handler->create_sessions_table();
+

If you create the table via the AWS Console, you need to make sure the primary key is a string. By default the DynamoDB Session Handler looks for a key named "id", so if you name the key something else, you will need to specify that when you configure the session handler (see the Configuration section below).

+

Once the session handler is registered with a valid table, you can write to (and read from) the session using the standard $_SESSION superglobal.

+
// Start the session. This will acquire a lock if session locking is enabled.
+session_start();
+
+// Alter the session data.
+$_SESSION['username'] = 'jeremy';
+$_SESSION['role'] = 'admin';
+
+// Close and write to the session.
+// REMEMBER: You should close the session ASAP to release the session lock.
+session_write_close();
+ +

Removing expired sessions

+

The session handler ties into PHP's native session garbage collection functionality, so expired sessions will be deleted periodically and automatically based on how you configure PHP to do the garbage collection. See the PHP manual for the following PHP INI settings: session.gc_probability, session.gc_divisor, and session.gc_maxlifetime. You may also manually call the DynamoDBSessionHandler::garbage_collect() to clean up the expired sessions.

+ +

Configuring the DynamoDB Session Handler

+

You may configure the behavior of the session handler using a the following settings:

+
+
table_name
+
The name of the DynamoDB table in which to store sessions.
+ +
hash_key
+
The name of the primary hash key in the DynamoDB sessions table.
+ +
session_lifetime
+
The lifetime of an inactive session before it should be garbage collected. If 0 is used, then the actual lifetime value that will be used is ini_get('session.gc_maxlifetime').
+ +
consistent_reads
+
Whether or not the session handler should do consistent reads from DynamoDB.
+ +
session_locking
+
Whether or not the session handler should do session locking (see the Session locking section for more information).
+ +
max_lock_wait_time
+
Maximum time, in seconds, that the session handler should take to acquire a lock before giving up. Only used if session_locking is true.
+ +
min_lock_retry_utime
+
Minimum time, in microseconds, that the session handler should wait to retry acquiring a lock. Only used if session_locking is true.
+ +
max_lock_retry_utime
+
Maximum time, in microseconds, that the session handler should wait to retry acquiring a lock. Only used if session_locking is true.
+
+ +

To configure the Session Handle, you must pass the configuration data into the constructor. (Note: The values used below are actually the default settings.)

+
$dynamodb = new AmazonDynamoDB();
+$handler = $dynamodb->register_session_handler(array(
+    'table_name'           => 'sessions',
+    'hash_key'             => 'id',
+    'session_lifetime'     => 0,
+    'consistent_reads'     => true,
+    'session_locking'      => true,
+    'max_lock_wait_time'   => 15,
+    'min_lock_retry_utime' => 5000,
+    'max_lock_retry_utime' => 50000,
+));
+ +

Session locking

+

The default session handler for PHP locks sessions using a pessimistic locking algorithm. If a request (process) opens a session for reading using the session_start() function, it first acquires a lock. The lock is closed when that request writes back to the session, either when the request is complete, or via the session_write_close() function. Since the DynamoDB Session Handler is meant to be a drop in replacement for the default session handler, it also implements the same locking scheme. However, this can be slow, undesirable, and costly when multiple, simultaneous requests from the same user occur, particularly with ajax requests or HTML iframes. In some cases, you may not want or need the session to be locked. After evaluating whether or not your application actually requires session locking, you may turn off locking when you configure the session handler by setting session_locking to false.

+ +

Pricing

+

Aside from nominal data storage and data transfer fees, the costs associated with using Amazon DynamoDB are calculated based on provisioned throughput capacity and item size (see the Amazon DynamoDB pricing details). Throughput is measured in units of Read Capacity and Write Capacity. Ultimately, the throughput and costs required for your sessions table is going to be based on your website traffic, but the following is a list of the capacity units required for each session-related operation:

+
    +
  • Reading via session_start() +

    With locking enabled: 1 unit of Write Capacity + 1 unit of Write Capacity for each time it must retry acquiring the lock

    +

    With locking disabed: 1 unit of Read Capacity (or 0.5 units of Read Capacity if consistent reads are disabled)

    +
  • +
  • Writing via session_write_close() +

    1 unit of Write Capacity

    +
  • +
  • Deleting via session_destroy() +

    1 unit of Write Capacity

    +
  • +
  • Garbage Collecting via DyanamoDBSessionHandler::garbage_collect() +

    0.5 units of Read Capacity per KB of data in the sessions table + 1 unit of Write Capacity per expired item

    +
  • +
+ +

More information

+

For more information on the Amazon DynamoDB service please visit the Amazon DynamoDB homepage.

+ + + diff --git a/3rdparty/aws-sdk/_docs/KNOWNISSUES.md b/3rdparty/aws-sdk/_docs/KNOWNISSUES.md new file mode 100644 index 0000000000..9773c3932b --- /dev/null +++ b/3rdparty/aws-sdk/_docs/KNOWNISSUES.md @@ -0,0 +1,65 @@ +# Known Issues + +## 2GB limit for 32-bit stacks; all Windows stacks. + +Because PHP's integer type is signed and many platforms use 32-bit integers, the AWS SDK for PHP does not correctly +handle files larger than 2GB on a 32-bit stack (where "stack" includes CPU, OS, web server, and PHP binary). This is a +[well-known PHP issue]. In the case of Microsoft® Windows®, there are no official builds of PHP that support 64-bit +integers. + +The recommended solution is to use a 64-bit Linux stack, such as the [64-bit Amazon Linux AMI] with the latest version of +PHP installed. + +For more information, please see: [PHP filesize: Return values]. A workaround is suggested in +`AmazonS3::create_mpu_object()` [with files bigger than 2GB]. + + [well-known PHP issue]: http://www.google.com/search?q=php+2gb+32-bit + [64-bit Amazon Linux AMI]: http://aws.amazon.com/amazon-linux-ami/ + [PHP filesize: Return values]: http://docs.php.net/manual/en/function.filesize.php#refsect1-function.filesize-returnvalues + [with files bigger than 2GB]: https://forums.aws.amazon.com/thread.jspa?messageID=215487#215487 + + +## Amazon S3 Buckets containing periods + +Amazon S3's SSL certificate covers domains that match `*.s3.amazonaws.com`. When buckets (e.g., `my-bucket`) are accessed +using DNS-style addressing (e.g., `my-bucket.s3.amazonaws.com`), those SSL/HTTPS connections are covered by the certificate. + +However, when a bucket name contains one or more periods (e.g., `s3.my-domain.com`) and is accessed using DNS-style +addressing (e.g., `s3.my-domain.com.s3.amazonaws.com`), that SSL/HTTPS connection will fail because the certificate +doesn't match. + +The most secure workaround is to change the bucket name to one that does not contain periods. Less secure workarounds +are to use `disable_ssl()` or `disable_ssl_verification()`. Because of the security implications, calling either of +these methods will throw a warning. You can avoid the warning by adjusting your `error_reporting()` settings. + + +## Expiring request signatures + +When leveraging `AmazonS3::create_mpu_object()`, it's possible that later parts of the multipart upload will fail if +the upload takes more than 15 minutes. + + +## Too many open file connections + +When leveraging `AmazonS3::create_mpu_object()`, it's possible that the SDK will attempt to open too many file resources +at once. Because the file connection limit is not available to the PHP environment, the SDK is unable to automatically +adjust the number of connections it attempts to open. + +A workaround is to increase the part size so that fewer file connections are opened. + + +## Exceptionally large batch requests + +When leveraging the batch request feature to execute multiple requests in parallel, it's possible that the SDK will +throw a fatal exception if a particular batch pool is exceptionally large and a service gets overloaded with requests. + +This seems to be most common when attempting to send a large number of emails with the SES service. + + +## Long-running processes using SSL leak memory + +When making requests with the SDK over SSL during long-running processes, there will be a gradual memory leak that can +eventually cause a crash. The leak occurs within the PHP bindings for cURL when attempting to verify the peer during an +SSL handshake. See for details about the bug. + +A workaround is to disable SSL for requests executed in long-running processes. diff --git a/3rdparty/aws-sdk/_docs/LICENSE.md b/3rdparty/aws-sdk/_docs/LICENSE.md new file mode 100644 index 0000000000..853ab3bae8 --- /dev/null +++ b/3rdparty/aws-sdk/_docs/LICENSE.md @@ -0,0 +1,151 @@ +# Apache License +Version 2.0, January 2004 + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + +## 1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 +through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the +License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled +by, or are under common control with that entity. For the purposes of this definition, "control" means +(i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract +or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial +ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software +source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, +including but not limited to compiled object code, generated documentation, and conversions to other media +types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, +as indicated by a copyright notice that is included in or attached to the work (an example is provided in the +Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) +the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, +as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not +include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work +and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any +modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to +Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to +submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of +electronic, verbal, or written communication sent to the Licensor or its representatives, including but not +limited to communication on electronic mailing lists, source code control systems, and issue tracking systems +that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but +excluding communication that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been +received by Licensor and subsequently incorporated within the Work. + + +## 2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare +Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + + +## 3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent +license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such +license applies only to those patent claims licensable by such Contributor that are necessarily infringed by +their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such +Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim +or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work +constitutes direct or contributory patent infringement, then any patent licenses granted to You under this +License for that Work shall terminate as of the date such litigation is filed. + + +## 4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without +modifications, and in Source or Object form, provided that You meet the following conditions: + + 1. You must give any other recipients of the Work or Derivative Works a copy of this License; and + + 2. You must cause any modified files to carry prominent notices stating that You changed the files; and + + 3. You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, + trademark, and attribution notices from the Source form of the Work, excluding those notices that do + not pertain to any part of the Derivative Works; and + + 4. If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that + You distribute must include a readable copy of the attribution notices contained within such NOTICE + file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed as part of the Derivative Works; within + the Source form or documentation, if provided along with the Derivative Works; or, within a display + generated by the Derivative Works, if and wherever such third-party notices normally appear. The + contents of the NOTICE file are for informational purposes only and do not modify the License. You may + add Your own attribution notices within Derivative Works that You distribute, alongside or as an + addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be + construed as modifying the License. + +You may add Your own copyright statement to Your modifications and may provide additional or different license +terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative +Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the +conditions stated in this License. + + +## 5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by +You to the Licensor shall be under the terms and conditions of this License, without any additional terms or +conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate +license agreement you may have executed with Licensor regarding such Contributions. + + +## 6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, service marks, or product names of +the Licensor, except as required for reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + + +## 7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor +provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express +or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, +MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + + +## 8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless +required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any +Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential +damages of any character arising as a result of this License or out of the use or inability to use the Work +(including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has been advised of the possibility +of such damages. + + +## 9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, +acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this +License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole +responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold +each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason +of your accepting any such warranty or additional liability. + + +END OF TERMS AND CONDITIONS diff --git a/3rdparty/aws-sdk/_docs/NOTICE.md b/3rdparty/aws-sdk/_docs/NOTICE.md new file mode 100644 index 0000000000..780c2e4c9d --- /dev/null +++ b/3rdparty/aws-sdk/_docs/NOTICE.md @@ -0,0 +1,444 @@ +# AWS SDK for PHP + +Based on [CloudFusion](http://getcloudfusion.com). Includes other third-party software. + +See below for complete copyright and licensing notices. + + +## AWS SDK for PHP + + + +Copyright 2010-2012 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"). +You may not use this file except in compliance with the License. +A copy of the License is located at + + + +or in the "license" file accompanying this file. This file is distributed +on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +express or implied. See the License for the specific language governing +permissions and limitations under the License. + + +## CloudFusion + + + +* Copyright 2005-2010 [Ryan Parman](http://ryanparman.com) +* Copyright 2007-2010 [Foleeo Inc.](http://warpshare.com) +* Copyright 2007-2010 "CONTRIBUTORS" (see [CONTRIBUTORS.md](CONTRIBUTORS.md) for a list) + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the organization nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + + + + +## CacheCore + + + +* Copyright 2007-2010 [Ryan Parman](http://ryanparman.com) +* Copyright 2007-2010 [Foleeo Inc.](http://warpshare.com) + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the organization nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + + + + +## RequestCore + + + +* Copyright 2007-2010 [Ryan Parman](http://ryanparman.com) +* Copyright 2007-2010 [Foleeo Inc.](http://warpshare.com) + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the organization nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + + + + +## SimplePie + + + +* Copyright 2004-2010 [Ryan Parman](http://ryanparman.com) +* Copyright 2005-2010 [Geoffrey Sneddon](http://gsnedders.com) +* Copyright 2008-2011 [Ryan McCue](http://ryanmccue.info) + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the organization nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + + + + +## Reqwest + + + +* Copyright 2011 [Dustin Diaz](http://dustindiaz.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + + + +## Human readable file sizes + + + +* Copyright 2004-2010 [Aidan Lister](http://aidanlister.com) +* Copyright 2007-2010 [Ryan Parman](http://ryanparman.com) + +Redistribution and use in source and binary forms, with or without +modification, is permitted provided that the following conditions +are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + 3. The name "PHP" must not be used to endorse or promote products + derived from this software without prior written permission. For + written permission, please contact group@php.net. + + 4. Products derived from this software may not be called "PHP", nor + may "PHP" appear in their name, without prior written permission + from group@php.net. You may indicate that your software works in + conjunction with PHP by saying "Foo for PHP" instead of calling + it "PHP Foo" or "phpfoo" + + 5. The PHP Group may publish revised and/or new versions of the + license from time to time. Each version will be given a + distinguishing version number. + Once covered code has been published under a particular version + of the license, you may always continue to use it under the terms + of that version. You may also choose to use such covered code + under the terms of any subsequent version of the license + published by the PHP Group. No one other than the PHP Group has + the right to modify the terms applicable to covered code created + under this License. + + 6. Redistributions of any form whatsoever must retain the following + acknowledgment: + "This product includes PHP software, freely available from + ". + +THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND +ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PHP +DEVELOPMENT TEAM OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +OF THE POSSIBILITY OF SUCH DAMAGE. + + + + +## Snippets from PHP.net documentation + +* `CFUtilities::is_base64()` - Copyright 2008 "debug" + +Redistribution and use in source and binary forms, with or without +modification, is permitted provided that the following conditions +are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + 3. The name "PHP" must not be used to endorse or promote products + derived from this software without prior written permission. For + written permission, please contact group@php.net. + + 4. Products derived from this software may not be called "PHP", nor + may "PHP" appear in their name, without prior written permission + from group@php.net. You may indicate that your software works in + conjunction with PHP by saying "Foo for PHP" instead of calling + it "PHP Foo" or "phpfoo" + + 5. The PHP Group may publish revised and/or new versions of the + license from time to time. Each version will be given a + distinguishing version number. + Once covered code has been published under a particular version + of the license, you may always continue to use it under the terms + of that version. You may also choose to use such covered code + under the terms of any subsequent version of the license + published by the PHP Group. No one other than the PHP Group has + the right to modify the terms applicable to covered code created + under this License. + + 6. Redistributions of any form whatsoever must retain the following + acknowledgment: + "This product includes PHP software, freely available from + ". + +THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND +ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PHP +DEVELOPMENT TEAM OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +OF THE POSSIBILITY OF SUCH DAMAGE. + + + + +## phunction PHP framework + + + +* Copyright 2010 [Alix Axel](mailto:alix-axel@users.sf.net) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + + + +## Symfony YAML Component + + + +Copyright 2008-2009 [Fabien Potencier](http://fabien.potencier.org) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + + + +## PEAR Console_ProgressBar + + + +Copyright 2003-2007 [Stefan Walk](http://pear.php.net/user/et) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + + + +## Mozilla Certificate Authority + +* +* + +The contents of this file are subject to the Mozilla Public License Version +1.1 (the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at +http://www.mozilla.org/MPL/ + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +for the specific language governing rights and limitations under the +License. + +The Original Code is the Netscape security libraries. + +The Initial Developer of the Original Code is Netscape Communications +Corporation. Portions created by the Initial Developer are Copyright +(C) 1994-2000 the Initial Developer. All Rights Reserved. + + + + +## array-to-domdocument + + + +* Copyright 2010-2011 [Omer Hassan](https://code.google.com/u/113495690012051782542/) +* Portions copyright 2012 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + diff --git a/3rdparty/aws-sdk/_docs/STREAMWRAPPER_README.html b/3rdparty/aws-sdk/_docs/STREAMWRAPPER_README.html new file mode 100644 index 0000000000..5d91068aa2 --- /dev/null +++ b/3rdparty/aws-sdk/_docs/STREAMWRAPPER_README.html @@ -0,0 +1,243 @@ + + + + + README + + + +

S3 Stream Wrapper

+

The S3 Stream Wrapper is a stream wrapper interface for PHP that provides access to Amazon S3 using PHP’s standard File System API functions.

+ +

What issues does this address?

+

There are a large number of existing applications, code snippets and other bits of PHP that are designed to read and write from the local file system as part of their normal operations. Many of these apps could benefit from moving into the cloud, but doing so would require rewriting a substantial amount of code.

+

What if we could simplify this process so that the updates required to make an existing application cloud-backed would be very minimal?

+ +

Proposed solution

+

PHP provides an interface for solving this exact kind of problem, called the Stream Wrapper interface. By writing a class that implements this interface and registering it as a handler we can reduce both the amount of rewriting that needs to be done for existing applications, as well as substantially lower the learning curve for reading and writing from Amazon S3.

+ +

How do I use it?

+

After including the AWS SDK for PHP in your project, use the AmazonS3::register_stream_wrapper() method to register s3:// as a supported stream wrapper for Amazon S3. It's that simple. Amazon S3 file patterns take the following form: s3://bucket/object.

+ +
require_once 'AWSSDKforPHP/sdk.class.php';
+
+$s3 = new AmazonS3();
+$s3->register_stream_wrapper();
+
+$directory = 's3://my-new-bucket';
+$filename = $directory . '/put.txt';
+$contents = '';
+
+if (mkdir($directory))
+{
+    if (file_put_contents($filename, 'This is some sample data.'))
+    {
+        $handle = fopen($filename, 'rb+');
+        $contents = stream_get_contents($handle);
+        fclose($handle);
+    }
+
+    rmdir($directory);
+}
+
+echo $contents;
+ +

You may also pass a different protocol name as a parameter to AmazonS3::register_stream_wrapper() if you want to use something besides s3://. Using this technique you can create more than one stream wrapper with different configurations (e.g. for different regions). To do that you just need to create separate instances of the AmazonS3 class, configure them, and then register a stream wrapper for each of them with different protocol names.

+ +
require_once 'AWSSDKforPHP/sdk.class.php';
+
+$s3east = new AmazonS3();
+$s3east->set_region(AmazonS3::REGION_US_E1);
+$s3east->register_stream_wrapper('s3east');
+mkdir('s3east://my-easterly-bucket');
+
+$s3west = new AmazonS3();
+$s3west->set_region(AmazonS3::REGION_US_W1);
+$s3west->register_stream_wrapper('s3west');
+mkdir('s3west://my-westerly-bucket');
+ +

Tests and usage examples

+

We are also including tests written in the PHPT format. Not only do these tests show how the software can be used, but any tests submitted back to us should be in this format. These tests will likely fail for you unless you change the bucket names to be globally unique across S3. You can run the tests with pear.

+
cd S3StreamWrapper/tests;
+pear run-tests;
+

If you have PHPUnit 3.6+ and Xdebug installed, you can generate a code coverage report as follows:

+
cd S3StreamWrapper/tests && \
+phpunit --colors --coverage-html ./_coverage_report . && \
+open ./_coverage_report/index.html;
+ +

Notes and Known Issues

+
    +
  • stream_lock() and stream_cast() are not currently implemented, and likely won't be.

  • +
  • Strangely touch() doesn’t seem to work. I think this is because of an issue with my implementation of url_stat(), but I can’t find any information on the magical combination of parameters that will make this work.

  • +
  • Using fopen() will always open in rb+ mode. Amazon S3 as a service doesn’t support anything else.

  • +
  • Because of the way that PHP interacts with the StreamWrapper interface, it’s difficult to optimize for batch requests under the hood. If you need to push or pull data from several objects, you may find that using batch requests with the standard interface has better latency.

  • +
  • rmdir() does not do a force-delete, so you will need to iterate over the files to delete them one-by-one.

  • +
  • realpath(), glob(), chmod(), chown(), chgrp(), tempnam() and a few other functions don’t support the StreamWrapper interface at the PHP-level because they're designed to work with the (no/low-latency) local file system.

  • +
  • Support for ftruncate() does not exist in any current release of PHP, but is implemented on the PHP trunk for a future release. http://bugs.php.net/53888.

  • +
  • Since Amazon S3 doesn’t support appending data, it is best to avoid functions that expect or rely on that functionality (e.g. fputcsv()).

  • +
+ +

Successfully tested with

+ + +

Known issues with

+ + +

A future version may provide S3-specific implementations of some of these functions (e.g., s3chmod(), s3glob(), s3touch()).

+ + diff --git a/3rdparty/aws-sdk/_docs/WHERE_IS_THE_API_REFERENCE.md b/3rdparty/aws-sdk/_docs/WHERE_IS_THE_API_REFERENCE.md new file mode 100644 index 0000000000..7ad235576b --- /dev/null +++ b/3rdparty/aws-sdk/_docs/WHERE_IS_THE_API_REFERENCE.md @@ -0,0 +1,2 @@ +You can find the API Reference at: +http://docs.amazonwebservices.com/AWSSDKforPHP/latest/ diff --git a/3rdparty/aws-sdk/authentication/signable.interface.php b/3rdparty/aws-sdk/authentication/signable.interface.php new file mode 100644 index 0000000000..5316d16a81 --- /dev/null +++ b/3rdparty/aws-sdk/authentication/signable.interface.php @@ -0,0 +1,48 @@ + class. + * + * @param string $endpoint (Required) The endpoint to direct the request to. + * @param string $operation (Required) The operation to execute as a result of this request. + * @param array $payload (Required) The options to use as part of the payload in the request. + * @param CFCredential $credentials (Required) The credentials to use for signing and making requests. + * @return void + */ + public function __construct($endpoint, $operation, $payload, CFCredential $credentials) + { + parent::__construct($endpoint, $operation, $payload, $credentials); + } + + /** + * Generates a cURL handle with all of the required authentication bits set. + * + * @return resource A cURL handle ready for executing. + */ + public function authenticate() + { + // Determine signing values + $current_time = time(); + $date = gmdate(CFUtilities::DATE_FORMAT_RFC2616, $current_time); + $timestamp = gmdate(CFUtilities::DATE_FORMAT_ISO8601, $current_time); + $query = array(); + + // Do we have an authentication token? + if ($this->auth_token) + { + $headers['X-Amz-Security-Token'] = $this->auth_token; + $query['SecurityToken'] = $this->auth_token; + } + + // Only add it if it exists. + if ($this->api_version) + { + $query['Version'] = $this->api_version; + } + + $query['Action'] = $this->operation; + $query['AWSAccessKeyId'] = $this->key; + $query['SignatureMethod'] = 'HmacSHA256'; + $query['SignatureVersion'] = 2; + $query['Timestamp'] = $timestamp; + + // Merge in any options that were passed in + if (is_array($this->payload)) + { + $query = array_merge($query, $this->payload); + } + + // Do a case-sensitive, natural order sort on the array keys. + uksort($query, 'strcmp'); + + // Create the string that needs to be hashed. + $canonical_query_string = $this->util->to_signable_string($query); + + // Remove the default scheme from the domain. + $domain = str_replace(array('http://', 'https://'), '', $this->endpoint); + + // Parse our request. + $parsed_url = parse_url('http://' . $domain); + + // Set the proper host header. + if (isset($parsed_url['port']) && (integer) $parsed_url['port'] !== 80 && (integer) $parsed_url['port'] !== 443) + { + $host_header = strtolower($parsed_url['host']) . ':' . $parsed_url['port']; + } + else + { + $host_header = strtolower($parsed_url['host']); + } + + // Set the proper request URI. + $request_uri = isset($parsed_url['path']) ? $parsed_url['path'] : '/'; + + // Prepare the string to sign + $this->string_to_sign = "POST\n$host_header\n$request_uri\n$canonical_query_string"; + + // Hash the AWS secret key and generate a signature for the request. + $query['Signature'] = base64_encode(hash_hmac('sha256', $this->string_to_sign, $this->secret_key, true)); + + // Generate the querystring from $query + $this->querystring = $this->util->to_query_string($query); + + // Gather information to pass along to other classes. + $helpers = array( + 'utilities' => $this->utilities_class, + 'request' => $this->request_class, + 'response' => $this->response_class, + ); + + // Compose the request. + $request_url = ($this->use_ssl ? 'https://' : 'http://') . $domain; + $request_url .= !isset($parsed_url['path']) ? '/' : ''; + + // Instantiate the request class + $request = new $this->request_class($request_url, $this->proxy, $helpers, $this->credentials); + $request->set_method('POST'); + $request->set_body($this->querystring); + $headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=utf-8'; + + // Pass along registered stream callbacks + if ($this->registered_streaming_read_callback) + { + $request->register_streaming_read_callback($this->registered_streaming_read_callback); + } + + if ($this->registered_streaming_write_callback) + { + $request->register_streaming_write_callback($this->registered_streaming_write_callback); + } + + // Sort headers + uksort($headers, 'strnatcasecmp'); + + // Add headers to request and compute the string to sign + foreach ($headers as $header_key => $header_value) + { + // Strip linebreaks from header values as they're illegal and can allow for security issues + $header_value = str_replace(array("\r", "\n"), '', $header_value); + + // Add the header if it has a value + if ($header_value !== '') + { + $request->add_header($header_key, $header_value); + } + } + + return $request; + } +} diff --git a/3rdparty/aws-sdk/authentication/signature_v3json.class.php b/3rdparty/aws-sdk/authentication/signature_v3json.class.php new file mode 100644 index 0000000000..d07f554d1f --- /dev/null +++ b/3rdparty/aws-sdk/authentication/signature_v3json.class.php @@ -0,0 +1,235 @@ + class. + * + * @param string $endpoint (Required) The endpoint to direct the request to. + * @param string $operation (Required) The operation to execute as a result of this request. + * @param array $payload (Required) The options to use as part of the payload in the request. + * @param CFCredential $credentials (Required) The credentials to use for signing and making requests. + * @return void + */ + public function __construct($endpoint, $operation, $payload, CFCredential $credentials) + { + parent::__construct($endpoint, $operation, $payload, $credentials); + } + + /** + * Generates a cURL handle with all of the required authentication bits set. + * + * @return resource A cURL handle ready for executing. + */ + public function authenticate() + { + // Determine signing values + $current_time = time(); + $date = gmdate(CFUtilities::DATE_FORMAT_RFC2616, $current_time); + $timestamp = gmdate(CFUtilities::DATE_FORMAT_ISO8601, $current_time); + $nonce = $this->util->generate_guid(); + $curlopts = array(); + $signed_headers = array(); + $return_curl_handle = false; + $x_amz_target = null; + $query = array('body' => $this->payload); + + // Do we have an authentication token? + if ($this->auth_token) + { + $headers['X-Amz-Security-Token'] = $this->auth_token; + $query['SecurityToken'] = $this->auth_token; + } + + // Manage the key-value pairs that are used in the query. + if (stripos($this->operation, 'x-amz-target') !== false) + { + $x_amz_target = trim(str_ireplace('x-amz-target:', '', $this->operation)); + } + else + { + $query['Action'] = $this->operation; + } + + // Only add it if it exists. + if ($this->api_version) + { + $query['Version'] = $this->api_version; + } + + $curlopts = array(); + + // Set custom CURLOPT settings + if (is_array($this->payload) && isset($this->payload['curlopts'])) + { + $curlopts = $this->payload['curlopts']; + unset($this->payload['curlopts']); + } + + // Merge in any options that were passed in + if (is_array($this->payload)) + { + $query = array_merge($query, $this->payload); + } + + $return_curl_handle = isset($query['returnCurlHandle']) ? $query['returnCurlHandle'] : false; + unset($query['returnCurlHandle']); + + // Do a case-sensitive, natural order sort on the array keys. + uksort($query, 'strcmp'); + + // Normalize JSON input + if (isset($query['body']) && $query['body'] === '[]') + { + $query['body'] = '{}'; + } + + // Create the string that needs to be hashed. + $canonical_query_string = $this->util->encode_signature2($query['body']); + + // Remove the default scheme from the domain. + $domain = str_replace(array('http://', 'https://'), '', $this->endpoint); + + // Parse our request. + $parsed_url = parse_url('http://' . $domain); + + // Set the proper host header. + if (isset($parsed_url['port']) && (integer) $parsed_url['port'] !== 80 && (integer) $parsed_url['port'] !== 443) + { + $host_header = strtolower($parsed_url['host']) . ':' . $parsed_url['port']; + } + else + { + $host_header = strtolower($parsed_url['host']); + } + + // Set the proper request URI. + $request_uri = isset($parsed_url['path']) ? $parsed_url['path'] : '/'; + + // Generate the querystring from $query + $this->querystring = $this->util->to_query_string($query); + + // Gather information to pass along to other classes. + $helpers = array( + 'utilities' => $this->utilities_class, + 'request' => $this->request_class, + 'response' => $this->response_class, + ); + + // Compose the request. + $request_url = ($this->use_ssl ? 'https://' : 'http://') . $domain; + $request_url .= !isset($parsed_url['path']) ? '/' : ''; + + // Instantiate the request class + $request = new $this->request_class($request_url, $this->proxy, $helpers, $this->credentials); + $request->set_method('POST'); + //$request->set_body($this->querystring); + //$headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=utf-8'; + + // Signing using X-Amz-Target is handled differently. + $headers['X-Amz-Target'] = $x_amz_target; + $headers['Content-Type'] = 'application/x-amz-json-1.0'; + $request->set_body($query['body']); + $this->querystring = $query['body']; + + // Pass along registered stream callbacks + if ($this->registered_streaming_read_callback) + { + $request->register_streaming_read_callback($this->registered_streaming_read_callback); + } + + if ($this->registered_streaming_write_callback) + { + $request->register_streaming_write_callback($this->registered_streaming_write_callback); + } + + // Add authentication headers + // $headers['X-Amz-Nonce'] = $nonce; + $headers['Date'] = $date; + $headers['Content-Length'] = strlen($this->querystring); + $headers['Content-MD5'] = $this->util->hex_to_base64(md5($this->querystring)); + $headers['Host'] = $host_header; + + // Sort headers + uksort($headers, 'strnatcasecmp'); + + // Prepare the string to sign (HTTP) + $this->string_to_sign = "POST\n$request_uri\n\n"; + + // Add headers to request and compute the string to sign + foreach ($headers as $header_key => $header_value) + { + // Strip linebreaks from header values as they're illegal and can allow for security issues + $header_value = str_replace(array("\r", "\n"), '', $header_value); + + // Add the header if it has a value + if ($header_value !== '') + { + $request->add_header($header_key, $header_value); + } + + // Generate the string to sign + if ( + substr(strtolower($header_key), 0, 8) === 'content-' || + strtolower($header_key) === 'date' || + strtolower($header_key) === 'expires' || + strtolower($header_key) === 'host' || + substr(strtolower($header_key), 0, 6) === 'x-amz-' + ) + { + $this->string_to_sign .= strtolower($header_key) . ':' . $header_value . "\n"; + $signed_headers[] = $header_key; + } + } + + $this->string_to_sign .= "\n"; + + if (isset($query['body']) && $query['body'] !== '') + { + $this->string_to_sign .= $query['body']; + } + + // Convert from string-to-sign to bytes-to-sign + $bytes_to_sign = hash('sha256', $this->string_to_sign, true); + + // Hash the AWS secret key and generate a signature for the request. + $signature = base64_encode(hash_hmac('sha256', $bytes_to_sign, $this->secret_key, true)); + + $headers['X-Amzn-Authorization'] = 'AWS3' + . ' AWSAccessKeyId=' . $this->key + . ',Algorithm=HmacSHA256' + . ',SignedHeaders=' . implode(';', $signed_headers) + . ',Signature=' . $signature; + + $request->add_header('X-Amzn-Authorization', $headers['X-Amzn-Authorization']); + $request->request_headers = $headers; + + return $request; + } +} diff --git a/3rdparty/aws-sdk/authentication/signature_v3query.class.php b/3rdparty/aws-sdk/authentication/signature_v3query.class.php new file mode 100644 index 0000000000..04565f928e --- /dev/null +++ b/3rdparty/aws-sdk/authentication/signature_v3query.class.php @@ -0,0 +1,192 @@ + class. + * + * @param string $endpoint (Required) The endpoint to direct the request to. + * @param string $operation (Required) The operation to execute as a result of this request. + * @param array $payload (Required) The options to use as part of the payload in the request. + * @param CFCredential $credentials (Required) The credentials to use for signing and making requests. + * @return void + */ + public function __construct($endpoint, $operation, $payload, CFCredential $credentials) + { + parent::__construct($endpoint, $operation, $payload, $credentials); + } + + /** + * Generates a cURL handle with all of the required authentication bits set. + * + * @return resource A cURL handle ready for executing. + */ + public function authenticate() + { + // Determine signing values + $current_time = time(); + $date = gmdate(CFUtilities::DATE_FORMAT_RFC2616, $current_time); + $timestamp = gmdate(CFUtilities::DATE_FORMAT_ISO8601, $current_time); + $nonce = $this->util->generate_guid(); + $curlopts = array(); + $signed_headers = array(); + + // Do we have an authentication token? + if ($this->auth_token) + { + $headers['X-Amz-Security-Token'] = $this->auth_token; + $query['SecurityToken'] = $this->auth_token; + } + + $query['Action'] = $this->operation; + $query['Version'] = $this->api_version; + + // Set custom CURLOPT settings + if (is_array($this->payload) && isset($this->payload['curlopts'])) + { + $curlopts = $this->payload['curlopts']; + unset($this->payload['curlopts']); + } + + // Merge in any options that were passed in + if (is_array($this->payload)) + { + $query = array_merge($query, $this->payload); + } + + $return_curl_handle = isset($query['returnCurlHandle']) ? $query['returnCurlHandle'] : false; + unset($query['returnCurlHandle']); + + // Do a case-sensitive, natural order sort on the array keys. + uksort($query, 'strcmp'); + $canonical_query_string = $this->util->to_signable_string($query); + + // Remove the default scheme from the domain. + $domain = str_replace(array('http://', 'https://'), '', $this->endpoint); + + // Parse our request. + $parsed_url = parse_url('http://' . $domain); + + // Set the proper host header. + if (isset($parsed_url['port']) && (integer) $parsed_url['port'] !== 80 && (integer) $parsed_url['port'] !== 443) + { + $host_header = strtolower($parsed_url['host']) . ':' . $parsed_url['port']; + } + else + { + $host_header = strtolower($parsed_url['host']); + } + + // Set the proper request URI. + $request_uri = isset($parsed_url['path']) ? $parsed_url['path'] : '/'; + + // Generate the querystring from $query + $this->querystring = $this->util->to_query_string($query); + + // Gather information to pass along to other classes. + $helpers = array( + 'utilities' => $this->utilities_class, + 'request' => $this->request_class, + 'response' => $this->response_class, + ); + + // Compose the request. + $request_url = ($this->use_ssl ? 'https://' : 'http://') . $domain; + $request_url .= !isset($parsed_url['path']) ? '/' : ''; + + // Instantiate the request class + $request = new $this->request_class($request_url, $this->proxy, $helpers, $this->credentials); + $request->set_method('POST'); + $request->set_body($this->querystring); + $headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=utf-8'; + + // Pass along registered stream callbacks + if ($this->registered_streaming_read_callback) + { + $request->register_streaming_read_callback($this->registered_streaming_read_callback); + } + + if ($this->registered_streaming_write_callback) + { + $request->register_streaming_write_callback($this->registered_streaming_write_callback); + } + + // Add authentication headers + $headers['X-Amz-Nonce'] = $nonce; + $headers['Date'] = $date; + $headers['Content-Length'] = strlen($this->querystring); + $headers['Content-MD5'] = $this->util->hex_to_base64(md5($this->querystring)); + $headers['Host'] = $host_header; + + // Sort headers + uksort($headers, 'strnatcasecmp'); + + // Prepare the string to sign (HTTPS) + $this->string_to_sign = $date . $nonce; + + // Add headers to request and compute the string to sign + foreach ($headers as $header_key => $header_value) + { + // Strip linebreaks from header values as they're illegal and can allow for security issues + $header_value = str_replace(array("\r", "\n"), '', $header_value); + + // Add the header if it has a value + if ($header_value !== '') + { + $request->add_header($header_key, $header_value); + } + + // Generate the string to sign + if ( + substr(strtolower($header_key), 0, 8) === 'content-' || + strtolower($header_key) === 'date' || + strtolower($header_key) === 'expires' || + strtolower($header_key) === 'host' || + substr(strtolower($header_key), 0, 6) === 'x-amz-' + ) + { + $signed_headers[] = $header_key; + } + } + + // Hash the AWS secret key and generate a signature for the request. + $signature = base64_encode(hash_hmac('sha256', $this->string_to_sign, $this->secret_key, true)); + + $headers['X-Amzn-Authorization'] = 'AWS3-HTTPS' + . ' AWSAccessKeyId=' . $this->key + . ',Algorithm=HmacSHA256' + . ',SignedHeaders=' . implode(';', $signed_headers) + . ',Signature=' . $signature; + + $request->add_header('X-Amzn-Authorization', $headers['X-Amzn-Authorization']); + $request->request_headers = $headers; + + return $request; + } +} diff --git a/3rdparty/aws-sdk/authentication/signature_v4json.class.php b/3rdparty/aws-sdk/authentication/signature_v4json.class.php new file mode 100644 index 0000000000..d3ab07ad8b --- /dev/null +++ b/3rdparty/aws-sdk/authentication/signature_v4json.class.php @@ -0,0 +1,353 @@ + class. + * + * @param string $endpoint (Required) The endpoint to direct the request to. + * @param string $operation (Required) The operation to execute as a result of this request. + * @param array $payload (Required) The options to use as part of the payload in the request. + * @param CFCredential $credentials (Required) The credentials to use for signing and making requests. + * @return void + */ + public function __construct($endpoint, $operation, $payload, CFCredential $credentials) + { + parent::__construct($endpoint, $operation, $payload, $credentials); + } + + /** + * Generates a cURL handle with all of the required authentication bits set. + * + * @return resource A cURL handle ready for executing. + */ + public function authenticate() + { + // Determine signing values + $current_time = time(); + $timestamp = gmdate(CFUtilities::DATE_FORMAT_SIGV4, $current_time); + + // Initialize + $x_amz_target = null; + + $this->headers = array(); + $this->signed_headers = array(); + $this->canonical_headers = array(); + $this->query = array(); + + // Prepare JSON structure + $decoded = json_decode($this->payload, true); + $data = (array) (is_array($decoded) ? $decoded : $this->payload); + unset($data['curlopts']); + unset($data['returnCurlHandle']); + $this->body = json_encode($data); + if ($this->body === '' || $this->body === '[]') + { + $this->body = '{}'; + } + + // Do we have an authentication token? + if ($this->auth_token) + { + $this->headers['X-Amz-Security-Token'] = $this->auth_token; + $this->query['SecurityToken'] = $this->auth_token; + } + + // Manage the key-value pairs that are used in the query. + if (stripos($this->operation, 'x-amz-target') !== false) + { + $x_amz_target = trim(str_ireplace('x-amz-target:', '', $this->operation)); + } + else + { + $this->query['Action'] = $this->operation; + } + + // Only add it if it exists. + if ($this->api_version) + { + $this->query['Version'] = $this->api_version; + } + + // Do a case-sensitive, natural order sort on the array keys. + uksort($this->query, 'strcmp'); + + // Remove the default scheme from the domain. + $domain = str_replace(array('http://', 'https://'), '', $this->endpoint); + + // Parse our request. + $parsed_url = parse_url('http://' . $domain); + + // Set the proper host header. + if (isset($parsed_url['port']) && (integer) $parsed_url['port'] !== 80 && (integer) $parsed_url['port'] !== 443) + { + $host_header = strtolower($parsed_url['host']) . ':' . $parsed_url['port']; + } + else + { + $host_header = strtolower($parsed_url['host']); + } + + // Generate the querystring from $this->query + $this->querystring = $this->util->to_query_string($this->query); + + // Gather information to pass along to other classes. + $helpers = array( + 'utilities' => $this->utilities_class, + 'request' => $this->request_class, + 'response' => $this->response_class, + ); + + // Compose the request. + $request_url = ($this->use_ssl ? 'https://' : 'http://') . $domain; + $request_url .= !isset($parsed_url['path']) ? '/' : ''; + + // Instantiate the request class + $request = new $this->request_class($request_url, $this->proxy, $helpers, $this->credentials); + $request->set_method('POST'); + $request->set_body($this->body); + $this->querystring = $this->body; + $this->headers['Content-Type'] = 'application/x-amz-json-1.1'; + $this->headers['X-Amz-Target'] = $x_amz_target; + + // Pass along registered stream callbacks + if ($this->registered_streaming_read_callback) + { + $request->register_streaming_read_callback($this->registered_streaming_read_callback); + } + + if ($this->registered_streaming_write_callback) + { + $request->register_streaming_write_callback($this->registered_streaming_write_callback); + } + + // Add authentication headers + $this->headers['X-Amz-Date'] = $timestamp; + $this->headers['Content-Length'] = strlen($this->querystring); + $this->headers['Host'] = $host_header; + + // Sort headers + uksort($this->headers, 'strnatcasecmp'); + + // Add headers to request and compute the string to sign + foreach ($this->headers as $header_key => $header_value) + { + // Strip linebreaks from header values as they're illegal and can allow for security issues + $header_value = str_replace(array("\r", "\n"), '', $header_value); + + $request->add_header($header_key, $header_value); + $this->canonical_headers[] = strtolower($header_key) . ':' . $header_value; + + $this->signed_headers[] = strtolower($header_key); + } + + $this->headers['Authorization'] = $this->authorization($timestamp); + $request->add_header('Authorization', $this->headers['Authorization']); + $request->request_headers = $this->headers; + + return $request; + } + + /** + * Generates the authorization string to use for the request. + * + * @param string $datetime (Required) The current timestamp. + * @return string The authorization string. + */ + protected function authorization($datetime) + { + $access_key_id = $this->key; + + $parts = array(); + $parts[] = "AWS4-HMAC-SHA256 Credential=${access_key_id}/" . $this->credential_string($datetime); + $parts[] = 'SignedHeaders=' . implode(';', $this->signed_headers); + $parts[] = 'Signature=' . $this->hex16($this->signature($datetime)); + + return implode(',', $parts); + } + + /** + * Calculate the signature. + * + * @param string $datetime (Required) The current timestamp. + * @return string The signature. + */ + protected function signature($datetime) + { + $k_date = $this->hmac('AWS4' . $this->secret_key, substr($datetime, 0, 8)); + $k_region = $this->hmac($k_date, $this->region()); + $k_service = $this->hmac($k_region, $this->service()); + $k_credentials = $this->hmac($k_service, 'aws4_request'); + $signature = $this->hmac($k_credentials, $this->string_to_sign($datetime)); + + return $signature; + } + + /** + * Calculate the string to sign. + * + * @param string $datetime (Required) The current timestamp. + * @return string The string to sign. + */ + protected function string_to_sign($datetime) + { + $parts = array(); + $parts[] = 'AWS4-HMAC-SHA256'; + $parts[] = $datetime; + $parts[] = $this->credential_string($datetime); + $parts[] = $this->hex16($this->hash($this->canonical_request())); + + $this->string_to_sign = implode("\n", $parts); + + return $this->string_to_sign; + } + + /** + * Generates the credential string to use for signing. + * + * @param string $datetime (Required) The current timestamp. + * @return string The credential string. + */ + protected function credential_string($datetime) + { + $parts = array(); + $parts[] = substr($datetime, 0, 8); + $parts[] = $this->region(); + $parts[] = $this->service(); + $parts[] = 'aws4_request'; + + return implode('/', $parts); + } + + /** + * Calculate the canonical request. + * + * @return string The canonical request. + */ + protected function canonical_request() + { + $parts = array(); + $parts[] = 'POST'; + $parts[] = $this->canonical_uri(); + $parts[] = ''; // $parts[] = $this->canonical_querystring(); + $parts[] = implode("\n", $this->canonical_headers) . "\n"; + $parts[] = implode(';', $this->signed_headers); + $parts[] = $this->hex16($this->hash($this->body)); + + $this->canonical_request = implode("\n", $parts); + + return $this->canonical_request; + } + + /** + * The region ID to use in the signature. + * + * @return return The region ID. + */ + protected function region() + { + $pieces = explode('.', $this->endpoint); + + // Handle cases with single/no region (i.e. service.region.amazonaws.com vs. service.amazonaws.com) + if (count($pieces < 4)) + { + return 'us-east-1'; + } + + return $pieces[1]; + } + + /** + * The service ID to use in the signature. + * + * @return return The service ID. + */ + protected function service() + { + $pieces = explode('.', $this->endpoint); + return $pieces[0]; + } + + /** + * The request URI path. + * + * @return string The request URI path. + */ + protected function canonical_uri() + { + return '/'; + } + + /** + * The canonical query string. + * + * @return string The canonical query string. + */ + protected function canonical_querystring() + { + if (!isset($this->canonical_querystring)) + { + $this->canonical_querystring = $this->util->to_signable_string($this->query); + } + + return $this->canonical_querystring; + } + + /** + * Hex16-pack the data. + * + * @param string $value (Required) The data to hex16 pack. + * @return string The hex16-packed data. + */ + protected function hex16($value) + { + $result = unpack('H*', $value); + return reset($result); + } + + /** + * Applies HMAC SHA-256 encryption to the string, salted by the key. + * + * @return string Raw HMAC SHA-256 hashed string. + */ + protected function hmac($key, $string) + { + return hash_hmac('sha256', $string, $key, true); + } + + /** + * SHA-256 hashes the string. + * + * @return string Raw SHA-256 hashed string. + */ + protected function hash($string) + { + return hash('sha256', $string, true); + } +} diff --git a/3rdparty/aws-sdk/authentication/signature_v4query.class.php b/3rdparty/aws-sdk/authentication/signature_v4query.class.php new file mode 100644 index 0000000000..bd5a3fbf5b --- /dev/null +++ b/3rdparty/aws-sdk/authentication/signature_v4query.class.php @@ -0,0 +1,345 @@ + class. + * + * @param string $endpoint (Required) The endpoint to direct the request to. + * @param string $operation (Required) The operation to execute as a result of this request. + * @param array $payload (Required) The options to use as part of the payload in the request. + * @param CFCredential $credentials (Required) The credentials to use for signing and making requests. + * @return void + */ + public function __construct($endpoint, $operation, $payload, CFCredential $credentials) + { + parent::__construct($endpoint, $operation, $payload, $credentials); + } + + /** + * Generates a cURL handle with all of the required authentication bits set. + * + * @return resource A cURL handle ready for executing. + */ + public function authenticate() + { + // Determine signing values + $current_time = time(); + $timestamp = gmdate(CFUtilities::DATE_FORMAT_SIGV4, $current_time); + + // Initialize + $x_amz_target = null; + + $this->headers = array(); + $this->signed_headers = array(); + $this->canonical_headers = array(); + $this->query = array('body' => is_array($this->payload) ? $this->payload : array()); + + // Do we have an authentication token? + if ($this->auth_token) + { + $this->headers['X-Amz-Security-Token'] = $this->auth_token; + $this->query['body']['SecurityToken'] = $this->auth_token; + } + + // Manage the key-value pairs that are used in the query. + if (stripos($this->operation, 'x-amz-target') !== false) + { + $x_amz_target = trim(str_ireplace('x-amz-target:', '', $this->operation)); + } + else + { + $this->query['body']['Action'] = $this->operation; + } + + // Only add it if it exists. + if ($this->api_version) + { + $this->query['body']['Version'] = $this->api_version; + } + + // Do a case-sensitive, natural order sort on the array keys. + uksort($this->query['body'], 'strcmp'); + + // Remove the default scheme from the domain. + $domain = str_replace(array('http://', 'https://'), '', $this->endpoint); + + // Parse our request. + $parsed_url = parse_url('http://' . $domain); + + // Set the proper host header. + if (isset($parsed_url['port']) && (integer) $parsed_url['port'] !== 80 && (integer) $parsed_url['port'] !== 443) + { + $host_header = strtolower($parsed_url['host']) . ':' . $parsed_url['port']; + } + else + { + $host_header = strtolower($parsed_url['host']); + } + + // Generate the querystring from $this->query + $this->querystring = $this->util->to_query_string($this->query); + + // Gather information to pass along to other classes. + $helpers = array( + 'utilities' => $this->utilities_class, + 'request' => $this->request_class, + 'response' => $this->response_class, + ); + + // Compose the request. + $request_url = ($this->use_ssl ? 'https://' : 'http://') . $domain; + $request_url .= !isset($parsed_url['path']) ? '/' : ''; + + // Instantiate the request class + $request = new $this->request_class($request_url, $this->proxy, $helpers, $this->credentials); + $request->set_method('POST'); + $request->set_body($this->canonical_querystring()); + $this->querystring = $this->canonical_querystring(); + + $this->headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=utf-8'; + $this->headers['X-Amz-Target'] = $x_amz_target; + + // Pass along registered stream callbacks + if ($this->registered_streaming_read_callback) + { + $request->register_streaming_read_callback($this->registered_streaming_read_callback); + } + + if ($this->registered_streaming_write_callback) + { + $request->register_streaming_write_callback($this->registered_streaming_write_callback); + } + + // Add authentication headers + $this->headers['X-Amz-Date'] = $timestamp; + $this->headers['Content-Length'] = strlen($this->querystring); + $this->headers['Content-MD5'] = $this->util->hex_to_base64(md5($this->querystring)); + $this->headers['Host'] = $host_header; + + // Sort headers + uksort($this->headers, 'strnatcasecmp'); + + // Add headers to request and compute the string to sign + foreach ($this->headers as $header_key => $header_value) + { + // Strip linebreaks from header values as they're illegal and can allow for security issues + $header_value = str_replace(array("\r", "\n"), '', $header_value); + + $request->add_header($header_key, $header_value); + $this->canonical_headers[] = strtolower($header_key) . ':' . $header_value; + + $this->signed_headers[] = strtolower($header_key); + } + + $this->headers['Authorization'] = $this->authorization($timestamp); + + $request->add_header('Authorization', $this->headers['Authorization']); + $request->request_headers = $this->headers; + + return $request; + } + + /** + * Generates the authorization string to use for the request. + * + * @param string $datetime (Required) The current timestamp. + * @return string The authorization string. + */ + protected function authorization($datetime) + { + $access_key_id = $this->key; + + $parts = array(); + $parts[] = "AWS4-HMAC-SHA256 Credential=${access_key_id}/" . $this->credential_string($datetime); + $parts[] = 'SignedHeaders=' . implode(';', $this->signed_headers); + $parts[] = 'Signature=' . $this->hex16($this->signature($datetime)); + + return implode(',', $parts); + } + + /** + * Calculate the signature. + * + * @param string $datetime (Required) The current timestamp. + * @return string The signature. + */ + protected function signature($datetime) + { + $k_date = $this->hmac('AWS4' . $this->secret_key, substr($datetime, 0, 8)); + $k_region = $this->hmac($k_date, $this->region()); + $k_service = $this->hmac($k_region, $this->service()); + $k_credentials = $this->hmac($k_service, 'aws4_request'); + $signature = $this->hmac($k_credentials, $this->string_to_sign($datetime)); + + return $signature; + } + + /** + * Calculate the string to sign. + * + * @param string $datetime (Required) The current timestamp. + * @return string The string to sign. + */ + protected function string_to_sign($datetime) + { + $parts = array(); + $parts[] = 'AWS4-HMAC-SHA256'; + $parts[] = $datetime; + $parts[] = $this->credential_string($datetime); + $parts[] = $this->hex16($this->hash($this->canonical_request())); + + $this->string_to_sign = implode("\n", $parts); + + return $this->string_to_sign; + } + + /** + * Generates the credential string to use for signing. + * + * @param string $datetime (Required) The current timestamp. + * @return string The credential string. + */ + protected function credential_string($datetime) + { + $parts = array(); + $parts[] = substr($datetime, 0, 8); + $parts[] = $this->region(); + $parts[] = $this->service(); + $parts[] = 'aws4_request'; + + return implode('/', $parts); + } + + /** + * Calculate the canonical request. + * + * @return string The canonical request. + */ + protected function canonical_request() + { + $parts = array(); + $parts[] = 'POST'; + $parts[] = $this->canonical_uri(); + $parts[] = ''; // $parts[] = $this->canonical_querystring(); + $parts[] = implode("\n", $this->canonical_headers) . "\n"; + $parts[] = implode(';', $this->signed_headers); + $parts[] = $this->hex16($this->hash($this->canonical_querystring())); + + $this->canonical_request = implode("\n", $parts); + + return $this->canonical_request; + } + + /** + * The region ID to use in the signature. + * + * @return return The region ID. + */ + protected function region() + { + $pieces = explode('.', $this->endpoint); + + // Handle cases with single/no region (i.e. service.region.amazonaws.com vs. service.amazonaws.com) + if (count($pieces < 4)) + { + return 'us-east-1'; + } + + return $pieces[1]; + } + + /** + * The service ID to use in the signature. + * + * @return return The service ID. + */ + protected function service() + { + $pieces = explode('.', $this->endpoint); + return ($pieces[0] === 'email') ? 'ses' : $pieces[0]; + } + + /** + * The request URI path. + * + * @return string The request URI path. + */ + protected function canonical_uri() + { + return '/'; + } + + /** + * The canonical query string. + * + * @return string The canonical query string. + */ + protected function canonical_querystring() + { + if (!isset($this->canonical_querystring)) + { + $this->canonical_querystring = $this->util->to_signable_string($this->query['body']); + } + + return $this->canonical_querystring; + } + + /** + * Hex16-pack the data. + * + * @param string $value (Required) The data to hex16 pack. + * @return string The hex16-packed data. + */ + protected function hex16($value) + { + $result = unpack('H*', $value); + return reset($result); + } + + /** + * Applies HMAC SHA-256 encryption to the string, salted by the key. + * + * @return string Raw HMAC SHA-256 hashed string. + */ + protected function hmac($key, $string) + { + return hash_hmac('sha256', $string, $key, true); + } + + /** + * SHA-256 hashes the string. + * + * @return string Raw SHA-256 hashed string. + */ + protected function hash($string) + { + return hash('sha256', $string, true); + } +} diff --git a/3rdparty/aws-sdk/authentication/signer.abstract.php b/3rdparty/aws-sdk/authentication/signer.abstract.php new file mode 100644 index 0000000000..f6bf7912f7 --- /dev/null +++ b/3rdparty/aws-sdk/authentication/signer.abstract.php @@ -0,0 +1,68 @@ +endpoint = $endpoint; + $this->operation = $operation; + $this->payload = $payload; + $this->credentials = $credentials; + } +} diff --git a/3rdparty/aws-sdk/lib/cachecore/LICENSE b/3rdparty/aws-sdk/lib/cachecore/LICENSE new file mode 100755 index 0000000000..49b38bd620 --- /dev/null +++ b/3rdparty/aws-sdk/lib/cachecore/LICENSE @@ -0,0 +1,25 @@ +Copyright (c) 2006-2010 Ryan Parman, Foleeo Inc., and contributors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, this list + of conditions and the following disclaimer in the documentation and/or other materials + provided with the distribution. + + * Neither the name of Ryan Parman, Foleeo Inc. nor the names of its contributors may be used to + endorse or promote products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS +OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS +AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/3rdparty/aws-sdk/lib/cachecore/README b/3rdparty/aws-sdk/lib/cachecore/README new file mode 100755 index 0000000000..07e00267cb --- /dev/null +++ b/3rdparty/aws-sdk/lib/cachecore/README @@ -0,0 +1 @@ +A simple caching system for PHP5 that provides a single interface for a variety of storage types. diff --git a/3rdparty/aws-sdk/lib/cachecore/_sql/README b/3rdparty/aws-sdk/lib/cachecore/_sql/README new file mode 100755 index 0000000000..e25d53d120 --- /dev/null +++ b/3rdparty/aws-sdk/lib/cachecore/_sql/README @@ -0,0 +1,5 @@ +The .sql files in this directory contain the code to create the tables for database caching. + +If you're not using database caching, you can safely ignore these. + +If you ARE using database caching, simply load the correct *.sql file into your database to set up the required tables. diff --git a/3rdparty/aws-sdk/lib/cachecore/_sql/mysql.sql b/3rdparty/aws-sdk/lib/cachecore/_sql/mysql.sql new file mode 100755 index 0000000000..2efee3a732 --- /dev/null +++ b/3rdparty/aws-sdk/lib/cachecore/_sql/mysql.sql @@ -0,0 +1,7 @@ +CREATE TABLE `cache` ( + `id` char(40) NOT NULL default '', + `expires` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP, + `data` longtext, + PRIMARY KEY (`id`), + UNIQUE KEY `id` (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 \ No newline at end of file diff --git a/3rdparty/aws-sdk/lib/cachecore/_sql/pgsql.sql b/3rdparty/aws-sdk/lib/cachecore/_sql/pgsql.sql new file mode 100755 index 0000000000..f2bdd86a52 --- /dev/null +++ b/3rdparty/aws-sdk/lib/cachecore/_sql/pgsql.sql @@ -0,0 +1,6 @@ +CREATE TABLE "cache" ( + expires timestamp without time zone NOT NULL, + id character(40) NOT NULL, + data text NOT NULL +) +WITH (OIDS=TRUE); \ No newline at end of file diff --git a/3rdparty/aws-sdk/lib/cachecore/_sql/sqlite3.sql b/3rdparty/aws-sdk/lib/cachecore/_sql/sqlite3.sql new file mode 100755 index 0000000000..590f45e4ff --- /dev/null +++ b/3rdparty/aws-sdk/lib/cachecore/_sql/sqlite3.sql @@ -0,0 +1,2 @@ +CREATE TABLE cache (id TEXT, expires NUMERIC, data BLOB); +CREATE UNIQUE INDEX idx ON cache(id ASC); \ No newline at end of file diff --git a/3rdparty/aws-sdk/lib/cachecore/cacheapc.class.php b/3rdparty/aws-sdk/lib/cachecore/cacheapc.class.php new file mode 100755 index 0000000000..59f5e88f39 --- /dev/null +++ b/3rdparty/aws-sdk/lib/cachecore/cacheapc.class.php @@ -0,0 +1,126 @@ +. Adheres + * to the ICacheCore interface. + * + * @version 2012.04.17 + * @copyright 2006-2012 Ryan Parman + * @copyright 2006-2010 Foleeo, Inc. + * @copyright 2012 Amazon.com, Inc. or its affiliates. + * @copyright 2008-2010 Contributors + * @license http://opensource.org/licenses/bsd-license.php Simplified BSD License + * @link http://github.com/skyzyx/cachecore CacheCore + * @link http://getcloudfusion.com CloudFusion + * @link http://php.net/apc APC + */ +class CacheAPC extends CacheCore implements ICacheCore +{ + + /*%******************************************************************************************%*/ + // CONSTRUCTOR + + /** + * Constructs a new instance of this class. + * + * @param string $name (Required) A name to uniquely identify the cache object. + * @param string $location (Optional) The location to store the cache object in. This may vary by cache method. The default value is NULL. + * @param integer $expires (Optional) The number of seconds until a cache object is considered stale. The default value is 0. + * @param boolean $gzip (Optional) Whether data should be gzipped before being stored. The default value is true. + * @return object Reference to the cache object. + */ + public function __construct($name, $location = null, $expires = 0, $gzip = true) + { + parent::__construct($name, null, $expires, $gzip); + $this->id = $this->name; + } + + /** + * Creates a new cache. + * + * @param mixed $data (Required) The data to cache. + * @return boolean Whether the operation was successful. + */ + public function create($data) + { + $data = serialize($data); + $data = $this->gzip ? gzcompress($data) : $data; + + return apc_add($this->id, $data, $this->expires); + } + + /** + * Reads a cache. + * + * @return mixed Either the content of the cache object, or boolean `false`. + */ + public function read() + { + if ($data = apc_fetch($this->id)) + { + $data = $this->gzip ? gzuncompress($data) : $data; + return unserialize($data); + } + + return false; + } + + /** + * Updates an existing cache. + * + * @param mixed $data (Required) The data to cache. + * @return boolean Whether the operation was successful. + */ + public function update($data) + { + $data = serialize($data); + $data = $this->gzip ? gzcompress($data) : $data; + + return apc_store($this->id, $data, $this->expires); + } + + /** + * Deletes a cache. + * + * @return boolean Whether the operation was successful. + */ + public function delete() + { + return apc_delete($this->id); + } + + /** + * Implemented here, but always returns `false`. APC manages its own expirations. + * + * @return boolean Whether the cache is expired or not. + */ + public function is_expired() + { + return false; + } + + /** + * Implemented here, but always returns `false`. APC manages its own expirations. + * + * @return mixed Either the Unix time stamp of the cache creation, or boolean `false`. + */ + public function timestamp() + { + return false; + } + + /** + * Implemented here, but always returns `false`. APC manages its own expirations. + * + * @return boolean Whether the operation was successful. + */ + public function reset() + { + return false; + } +} + + +/*%******************************************************************************************%*/ +// EXCEPTIONS + +class CacheAPC_Exception extends CacheCore_Exception {} diff --git a/3rdparty/aws-sdk/lib/cachecore/cachecore.class.php b/3rdparty/aws-sdk/lib/cachecore/cachecore.class.php new file mode 100755 index 0000000000..1670d31640 --- /dev/null +++ b/3rdparty/aws-sdk/lib/cachecore/cachecore.class.php @@ -0,0 +1,160 @@ +name = $name; + $this->location = $location; + $this->expires = $expires; + $this->gzip = $gzip; + + return $this; + } + + /** + * Allows for chaining from the constructor. Requires PHP 5.3 or newer. + * + * @param string $name (Required) A name to uniquely identify the cache object. + * @param string $location (Optional) The location to store the cache object in. This may vary by cache method. The default value is NULL. + * @param integer $expires (Optional) The number of seconds until a cache object is considered stale. The default value is 0. + * @param boolean $gzip (Optional) Whether data should be gzipped before being stored. The default value is true. + * @return object Reference to the cache object. + */ + public static function init($name, $location = null, $expires = 0, $gzip = true) + { + if (version_compare(PHP_VERSION, '5.3.0', '<')) + { + throw new Exception('PHP 5.3 or newer is required to use CacheCore::init().'); + } + + $self = get_called_class(); + return new $self($name, $location, $expires, $gzip); + } + + /** + * Set the number of seconds until a cache expires. + * + * @param integer $expires (Optional) The number of seconds until a cache object is considered stale. The default value is 0. + * @return $this + */ + public function expire_in($seconds) + { + $this->expires = $seconds; + return $this; + } + + /** + * Provides a simple, straightforward cache-logic mechanism. Useful for non-complex response caches. + * + * @param string|function $callback (Required) The name of the function to fire when we need to fetch new data to cache. + * @param array params (Optional) Parameters to pass into the callback function, as an array. + * @return array The cached data being requested. + */ + public function response_manager($callback, $params = null) + { + // Automatically handle $params values. + $params = is_array($params) ? $params : array($params); + + if ($data = $this->read()) + { + if ($this->is_expired()) + { + if ($data = call_user_func_array($callback, $params)) + { + $this->update($data); + } + else + { + $this->reset(); + $data = $this->read(); + } + } + } + else + { + if ($data = call_user_func_array($callback, $params)) + { + $this->create($data); + } + } + + return $data; + } +} + + +/*%******************************************************************************************%*/ +// CORE DEPENDENCIES + +// Include the ICacheCore interface. +if (file_exists(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'icachecore.interface.php')) +{ + include_once dirname(__FILE__) . DIRECTORY_SEPARATOR . 'icachecore.interface.php'; +} + + +/*%******************************************************************************************%*/ +// EXCEPTIONS + +class CacheCore_Exception extends Exception {} diff --git a/3rdparty/aws-sdk/lib/cachecore/cachefile.class.php b/3rdparty/aws-sdk/lib/cachecore/cachefile.class.php new file mode 100755 index 0000000000..3df240151f --- /dev/null +++ b/3rdparty/aws-sdk/lib/cachecore/cachefile.class.php @@ -0,0 +1,189 @@ +. Adheres + * to the ICacheCore interface. + * + * @version 2012.04.17 + * @copyright 2006-2012 Ryan Parman + * @copyright 2006-2010 Foleeo, Inc. + * @copyright 2012 Amazon.com, Inc. or its affiliates. + * @copyright 2008-2010 Contributors + * @license http://opensource.org/licenses/bsd-license.php Simplified BSD License + * @link http://github.com/skyzyx/cachecore CacheCore + * @link http://getcloudfusion.com CloudFusion + */ +class CacheFile extends CacheCore implements ICacheCore +{ + + /*%******************************************************************************************%*/ + // CONSTRUCTOR + + /** + * Constructs a new instance of this class. + * + * @param string $name (Required) A name to uniquely identify the cache object. + * @param string $location (Optional) The location to store the cache object in. This may vary by cache method. The default value is NULL. + * @param integer $expires (Optional) The number of seconds until a cache object is considered stale. The default value is 0. + * @param boolean $gzip (Optional) Whether data should be gzipped before being stored. The default value is true. + * @return object Reference to the cache object. + */ + public function __construct($name, $location = null, $expires = 0, $gzip = true) + { + parent::__construct($name, $location, $expires, $gzip); + $this->id = $this->location . '/' . $this->name . '.cache'; + } + + /** + * Creates a new cache. + * + * @param mixed $data (Required) The data to cache. + * @return boolean Whether the operation was successful. + */ + public function create($data) + { + if (file_exists($this->id)) + { + return false; + } + elseif (realpath($this->location) && file_exists($this->location) && is_writeable($this->location)) + { + $data = serialize($data); + $data = $this->gzip ? gzcompress($data) : $data; + + return (bool) file_put_contents($this->id, $data); + } + elseif (realpath($this->location) && file_exists($this->location)) + { + throw new CacheFile_Exception('The file system location "' . $this->location . '" is not writable. Check the file system permissions for this directory.'); + } + else + { + throw new CacheFile_Exception('The file system location "' . $this->location . '" does not exist. Create the directory, or double-check any relative paths that may have been set.'); + } + + return false; + } + + /** + * Reads a cache. + * + * @return mixed Either the content of the cache object, or boolean `false`. + */ + public function read() + { + if (file_exists($this->id) && is_readable($this->id)) + { + $data = file_get_contents($this->id); + $data = $this->gzip ? gzuncompress($data) : $data; + $data = unserialize($data); + + if ($data === false) + { + /* + This should only happen when someone changes the gzip settings and there is + existing data or someone has been mucking about in the cache folder manually. + Delete the bad entry since the file cache doesn't clean up after itself and + then return false so fresh data will be retrieved. + */ + $this->delete(); + return false; + } + + return $data; + } + + return false; + } + + /** + * Updates an existing cache. + * + * @param mixed $data (Required) The data to cache. + * @return boolean Whether the operation was successful. + */ + public function update($data) + { + if (file_exists($this->id) && is_writeable($this->id)) + { + $data = serialize($data); + $data = $this->gzip ? gzcompress($data) : $data; + + return (bool) file_put_contents($this->id, $data); + } + else + { + throw new CacheFile_Exception('The file system location is not writeable. Check your file system permissions and ensure that the cache directory exists.'); + } + + return false; + } + + /** + * Deletes a cache. + * + * @return boolean Whether the operation was successful. + */ + public function delete() + { + if (file_exists($this->id)) + { + return unlink($this->id); + } + + return false; + } + + /** + * Checks whether the cache object is expired or not. + * + * @return boolean Whether the cache is expired or not. + */ + public function is_expired() + { + if ($this->timestamp() + $this->expires < time()) + { + return true; + } + + return false; + } + + /** + * Retrieves the timestamp of the cache. + * + * @return mixed Either the Unix time stamp of the cache creation, or boolean `false`. + */ + public function timestamp() + { + clearstatcache(); + + if (file_exists($this->id)) + { + $this->timestamp = filemtime($this->id); + return $this->timestamp; + } + + return false; + } + + /** + * Resets the freshness of the cache. + * + * @return boolean Whether the operation was successful. + */ + public function reset() + { + if (file_exists($this->id)) + { + return touch($this->id); + } + + return false; + } +} + + +/*%******************************************************************************************%*/ +// EXCEPTIONS + +class CacheFile_Exception extends CacheCore_Exception {} diff --git a/3rdparty/aws-sdk/lib/cachecore/cachemc.class.php b/3rdparty/aws-sdk/lib/cachecore/cachemc.class.php new file mode 100755 index 0000000000..5b0f8a9306 --- /dev/null +++ b/3rdparty/aws-sdk/lib/cachecore/cachemc.class.php @@ -0,0 +1,183 @@ +. Adheres + * to the ICacheCore interface. + * + * @version 2012.04.17 + * @copyright 2006-2012 Ryan Parman + * @copyright 2006-2010 Foleeo, Inc. + * @copyright 2012 Amazon.com, Inc. or its affiliates. + * @copyright 2008-2010 Contributors + * @license http://opensource.org/licenses/bsd-license.php Simplified BSD License + * @link http://github.com/skyzyx/cachecore CacheCore + * @link http://getcloudfusion.com CloudFusion + * @link http://php.net/memcache Memcache + * @link http://php.net/memcached Memcached + */ +class CacheMC extends CacheCore implements ICacheCore +{ + /** + * Holds the Memcache object. + */ + var $memcache = null; + + /** + * Whether the Memcached extension is being used (as opposed to Memcache). + */ + var $is_memcached = false; + + + /*%******************************************************************************************%*/ + // CONSTRUCTOR + + /** + * Constructs a new instance of this class. + * + * @param string $name (Required) A name to uniquely identify the cache object. + * @param string $location (Optional) The location to store the cache object in. This may vary by cache method. The default value is NULL. + * @param integer $expires (Optional) The number of seconds until a cache object is considered stale. The default value is 0. + * @param boolean $gzip (Optional) Whether data should be gzipped before being stored. The default value is true. + * @return object Reference to the cache object. + */ + public function __construct($name, $location = null, $expires = 0, $gzip = true) + { + parent::__construct($name, null, $expires, $gzip); + $this->id = $this->name; + + // Prefer Memcached over Memcache. + if (class_exists('Memcached')) + { + $this->memcache = new Memcached(); + $this->is_memcached = true; + } + elseif (class_exists('Memcache')) + { + $this->memcache = new Memcache(); + } + else + { + return false; + } + + // Enable compression, if available + if ($this->gzip) + { + if ($this->is_memcached) + { + $this->memcache->setOption(Memcached::OPT_COMPRESSION, true); + } + else + { + $this->gzip = MEMCACHE_COMPRESSED; + } + } + + // Process Memcached servers. + if (isset($location) && sizeof($location) > 0) + { + foreach ($location as $loc) + { + if (isset($loc['port']) && !empty($loc['port'])) + { + $this->memcache->addServer($loc['host'], $loc['port']); + } + else + { + $this->memcache->addServer($loc['host'], 11211); + } + } + } + + return $this; + } + + /** + * Creates a new cache. + * + * @param mixed $data (Required) The data to cache. + * @return boolean Whether the operation was successful. + */ + public function create($data) + { + if ($this->is_memcached) + { + return $this->memcache->set($this->id, $data, $this->expires); + } + return $this->memcache->set($this->id, $data, $this->gzip, $this->expires); + } + + /** + * Reads a cache. + * + * @return mixed Either the content of the cache object, or boolean `false`. + */ + public function read() + { + if ($this->is_memcached) + { + return $this->memcache->get($this->id); + } + return $this->memcache->get($this->id, $this->gzip); + } + + /** + * Updates an existing cache. + * + * @param mixed $data (Required) The data to cache. + * @return boolean Whether the operation was successful. + */ + public function update($data) + { + if ($this->is_memcached) + { + return $this->memcache->replace($this->id, $data, $this->expires); + } + return $this->memcache->replace($this->id, $data, $this->gzip, $this->expires); + } + + /** + * Deletes a cache. + * + * @return boolean Whether the operation was successful. + */ + public function delete() + { + return $this->memcache->delete($this->id); + } + + /** + * Implemented here, but always returns `false`. Memcache manages its own expirations. + * + * @return boolean Whether the cache is expired or not. + */ + public function is_expired() + { + return false; + } + + /** + * Implemented here, but always returns `false`. Memcache manages its own expirations. + * + * @return mixed Either the Unix time stamp of the cache creation, or boolean `false`. + */ + public function timestamp() + { + return false; + } + + /** + * Implemented here, but always returns `false`. Memcache manages its own expirations. + * + * @return boolean Whether the operation was successful. + */ + public function reset() + { + return false; + } +} + + +/*%******************************************************************************************%*/ +// EXCEPTIONS + +class CacheMC_Exception extends CacheCore_Exception {} diff --git a/3rdparty/aws-sdk/lib/cachecore/cachepdo.class.php b/3rdparty/aws-sdk/lib/cachecore/cachepdo.class.php new file mode 100755 index 0000000000..5716021d8f --- /dev/null +++ b/3rdparty/aws-sdk/lib/cachecore/cachepdo.class.php @@ -0,0 +1,297 @@ +. Adheres + * to the ICacheCore interface. + * + * @version 2012.04.17 + * @copyright 2006-2012 Ryan Parman + * @copyright 2006-2010 Foleeo, Inc. + * @copyright 2012 Amazon.com, Inc. or its affiliates. + * @copyright 2008-2010 Contributors + * @license http://opensource.org/licenses/bsd-license.php Simplified BSD License + * @link http://github.com/skyzyx/cachecore CacheCore + * @link http://getcloudfusion.com CloudFusion + * @link http://php.net/pdo PDO + */ +class CachePDO extends CacheCore implements ICacheCore +{ + /** + * Reference to the PDO connection object. + */ + var $pdo = null; + + /** + * Holds the parsed URL components. + */ + var $dsn = null; + + /** + * Holds the PDO-friendly version of the connection string. + */ + var $dsn_string = null; + + /** + * Holds the prepared statement for creating an entry. + */ + var $create = null; + + /** + * Holds the prepared statement for reading an entry. + */ + var $read = null; + + /** + * Holds the prepared statement for updating an entry. + */ + var $update = null; + + /** + * Holds the prepared statement for resetting the expiry of an entry. + */ + var $reset = null; + + /** + * Holds the prepared statement for deleting an entry. + */ + var $delete = null; + + /** + * Holds the response of the read so we only need to fetch it once instead of doing + * multiple queries. + */ + var $store_read = null; + + + /*%******************************************************************************************%*/ + // CONSTRUCTOR + + /** + * Constructs a new instance of this class. + * + * Tested with [MySQL 5.0.x](http://mysql.com), [PostgreSQL](http://postgresql.com), and + * [SQLite 3.x](http://sqlite.org). SQLite 2.x is assumed to work. No other PDO-supported databases have + * been tested (e.g. Oracle, Microsoft SQL Server, IBM DB2, ODBC, Sybase, Firebird). Feel free to send + * patches for additional database support. + * + * See for more information. + * + * @param string $name (Required) A name to uniquely identify the cache object. + * @param string $location (Optional) The location to store the cache object in. This may vary by cache method. The default value is NULL. + * @param integer $expires (Optional) The number of seconds until a cache object is considered stale. The default value is 0. + * @param boolean $gzip (Optional) Whether data should be gzipped before being stored. The default value is true. + * @return object Reference to the cache object. + */ + public function __construct($name, $location = null, $expires = 0, $gzip = true) + { + // Make sure the name is no longer than 40 characters. + $name = sha1($name); + + // Call parent constructor and set id. + parent::__construct($name, $location, $expires, $gzip); + $this->id = $this->name; + $options = array(); + + // Check if the location contains :// (e.g. mysql://user:pass@hostname:port/table) + if (stripos($location, '://') === false) + { + // No? Just pass it through. + $this->dsn = parse_url($location); + $this->dsn_string = $location; + } + else + { + // Yes? Parse and set the DSN + $this->dsn = parse_url($location); + $this->dsn_string = $this->dsn['scheme'] . ':host=' . $this->dsn['host'] . ((isset($this->dsn['port'])) ? ';port=' . $this->dsn['port'] : '') . ';dbname=' . substr($this->dsn['path'], 1); + } + + // Make sure that user/pass are defined. + $user = isset($this->dsn['user']) ? $this->dsn['user'] : null; + $pass = isset($this->dsn['pass']) ? $this->dsn['pass'] : null; + + // Set persistence for databases that support it. + switch ($this->dsn['scheme']) + { + case 'mysql': // MySQL + case 'pgsql': // PostgreSQL + $options[PDO::ATTR_PERSISTENT] = true; + break; + } + + // Instantiate a new PDO object with a persistent connection. + $this->pdo = new PDO($this->dsn_string, $user, $pass, $options); + + // Define prepared statements for improved performance. + $this->create = $this->pdo->prepare("INSERT INTO cache (id, expires, data) VALUES (:id, :expires, :data)"); + $this->read = $this->pdo->prepare("SELECT id, expires, data FROM cache WHERE id = :id"); + $this->reset = $this->pdo->prepare("UPDATE cache SET expires = :expires WHERE id = :id"); + $this->delete = $this->pdo->prepare("DELETE FROM cache WHERE id = :id"); + } + + /** + * Creates a new cache. + * + * @param mixed $data (Required) The data to cache. + * @return boolean Whether the operation was successful. + */ + public function create($data) + { + $data = serialize($data); + $data = $this->gzip ? gzcompress($data) : $data; + + $this->create->bindParam(':id', $this->id); + $this->create->bindParam(':data', $data); + $this->create->bindParam(':expires', $this->generate_timestamp()); + + return (bool) $this->create->execute(); + } + + /** + * Reads a cache. + * + * @return mixed Either the content of the cache object, or boolean `false`. + */ + public function read() + { + if (!$this->store_read) + { + $this->read->bindParam(':id', $this->id); + $this->read->execute(); + $this->store_read = $this->read->fetch(PDO::FETCH_ASSOC); + } + + if ($this->store_read) + { + $data = $this->store_read['data']; + $data = $this->gzip ? gzuncompress($data) : $data; + + return unserialize($data); + } + + return false; + } + + /** + * Updates an existing cache. + * + * @param mixed $data (Required) The data to cache. + * @return boolean Whether the operation was successful. + */ + public function update($data) + { + $this->delete(); + return $this->create($data); + } + + /** + * Deletes a cache. + * + * @return boolean Whether the operation was successful. + */ + public function delete() + { + $this->delete->bindParam(':id', $this->id); + return $this->delete->execute(); + } + + /** + * Checks whether the cache object is expired or not. + * + * @return boolean Whether the cache is expired or not. + */ + public function is_expired() + { + if ($this->timestamp() + $this->expires < time()) + { + return true; + } + + return false; + } + + /** + * Retrieves the timestamp of the cache. + * + * @return mixed Either the Unix time stamp of the cache creation, or boolean `false`. + */ + public function timestamp() + { + if (!$this->store_read) + { + $this->read->bindParam(':id', $this->id); + $this->read->execute(); + $this->store_read = $this->read->fetch(PDO::FETCH_ASSOC); + } + + if ($this->store_read) + { + $value = $this->store_read['expires']; + + // If 'expires' isn't yet an integer, convert it into one. + if (!is_numeric($value)) + { + $value = strtotime($value); + } + + $this->timestamp = date('U', $value); + return $this->timestamp; + } + + return false; + } + + /** + * Resets the freshness of the cache. + * + * @return boolean Whether the operation was successful. + */ + public function reset() + { + $this->reset->bindParam(':id', $this->id); + $this->reset->bindParam(':expires', $this->generate_timestamp()); + return (bool) $this->reset->execute(); + } + + /** + * Returns a list of supported PDO database drivers. Identical to . + * + * @return array The list of supported database drivers. + * @link http://php.net/pdo.getavailabledrivers PHP Method + */ + public function get_drivers() + { + return PDO::getAvailableDrivers(); + } + + /** + * Returns a timestamp value apropriate to the current database type. + * + * @return mixed Timestamp for MySQL and PostgreSQL, integer value for SQLite. + */ + protected function generate_timestamp() + { + // Define 'expires' settings differently. + switch ($this->dsn['scheme']) + { + // These support timestamps. + case 'mysql': // MySQL + case 'pgsql': // PostgreSQL + $expires = date(DATE_FORMAT_MYSQL, time()); + break; + + // These support integers. + case 'sqlite': // SQLite 3 + case 'sqlite2': // SQLite 2 + $expires = time(); + break; + } + + return $expires; + } +} + + +/*%******************************************************************************************%*/ +// EXCEPTIONS + +class CachePDO_Exception extends CacheCore_Exception {} diff --git a/3rdparty/aws-sdk/lib/cachecore/cachexcache.class.php b/3rdparty/aws-sdk/lib/cachecore/cachexcache.class.php new file mode 100755 index 0000000000..a0f279aaea --- /dev/null +++ b/3rdparty/aws-sdk/lib/cachecore/cachexcache.class.php @@ -0,0 +1,129 @@ +. Adheres + * to the ICacheCore interface. + * + * @version 2012.04.17 + * @copyright 2006-2012 Ryan Parman + * @copyright 2006-2010 Foleeo, Inc. + * @copyright 2012 Amazon.com, Inc. or its affiliates. + * @copyright 2008-2010 Contributors + * @license http://opensource.org/licenses/bsd-license.php Simplified BSD License + * @link http://github.com/skyzyx/cachecore CacheCore + * @link http://getcloudfusion.com CloudFusion + * @link http://xcache.lighttpd.net XCache + */ +class CacheXCache extends CacheCore implements ICacheCore +{ + + /*%******************************************************************************************%*/ + // CONSTRUCTOR + + /** + * Constructs a new instance of this class. + * + * @param string $name (Required) A name to uniquely identify the cache object. + * @param string $location (Optional) The location to store the cache object in. This may vary by cache method. The default value is NULL. + * @param integer $expires (Optional) The number of seconds until a cache object is considered stale. The default value is 0. + * @param boolean $gzip (Optional) Whether data should be gzipped before being stored. The default value is true. + * @return object Reference to the cache object. + */ + public function __construct($name, $location = null, $expires = 0, $gzip = true) + { + parent::__construct($name, null, $expires, $gzip); + $this->id = $this->name; + } + + /** + * Creates a new cache. + * + * @param mixed $data (Required) The data to cache. + * @return boolean Whether the operation was successful. + */ + public function create($data) + { + $data = serialize($data); + $data = $this->gzip ? gzcompress($data) : $data; + + return xcache_set($this->id, $data, $this->expires); + } + + /** + * Reads a cache. + * + * @return mixed Either the content of the cache object, or boolean `false`. + */ + public function read() + { + if ($data = xcache_get($this->id)) + { + $data = $this->gzip ? gzuncompress($data) : $data; + return unserialize($data); + } + + return false; + } + + /** + * Updates an existing cache. + * + * @param mixed $data (Required) The data to cache. + * @return boolean Whether the operation was successful. + */ + public function update($data) + { + $data = serialize($data); + $data = $this->gzip ? gzcompress($data) : $data; + + return xcache_set($this->id, $data, $this->expires); + } + + /** + * Deletes a cache. + * + * @return boolean Whether the operation was successful. + */ + public function delete() + { + return xcache_unset($this->id); + } + + /** + * Defined here, but always returns false. XCache manages it's own expirations. It's worth + * mentioning that if the server is configured for a long xcache.var_gc_interval then it IS + * possible for expired data to remain in the var cache, though it is not possible to access + * it. + * + * @return boolean Whether the cache is expired or not. + */ + public function is_expired() + { + return false; + } + + /** + * Implemented here, but always returns `false`. XCache manages its own expirations. + * + * @return mixed Either the Unix time stamp of the cache creation, or boolean `false`. + */ + public function timestamp() + { + return false; + } + + /** + * Implemented here, but always returns `false`. XCache manages its own expirations. + * + * @return boolean Whether the operation was successful. + */ + public function reset() + { + return false; + } +} + + +/*%******************************************************************************************%*/ +// EXCEPTIONS + +class CacheXCache_Exception extends CacheCore_Exception {} diff --git a/3rdparty/aws-sdk/lib/cachecore/icachecore.interface.php b/3rdparty/aws-sdk/lib/cachecore/icachecore.interface.php new file mode 100755 index 0000000000..8d49f5bf49 --- /dev/null +++ b/3rdparty/aws-sdk/lib/cachecore/icachecore.interface.php @@ -0,0 +1,66 @@ + class. + * + * @version 2009.03.22 + * @copyright 2006-2010 Ryan Parman + * @copyright 2006-2010 Foleeo, Inc. + * @copyright 2008-2010 Contributors + * @license http://opensource.org/licenses/bsd-license.php Simplified BSD License + * @link http://github.com/skyzyx/cachecore CacheCore + * @link http://getcloudfusion.com CloudFusion + */ +interface ICacheCore +{ + /** + * Creates a new cache. + * + * @param mixed $data (Required) The data to cache. + * @return boolean Whether the operation was successful. + */ + public function create($data); + + /** + * Reads a cache. + * + * @return mixed Either the content of the cache object, or boolean `false`. + */ + public function read(); + + /** + * Updates an existing cache. + * + * @param mixed $data (Required) The data to cache. + * @return boolean Whether the operation was successful. + */ + public function update($data); + + /** + * Deletes a cache. + * + * @return boolean Whether the operation was successful. + */ + public function delete(); + + /** + * Checks whether the cache object is expired or not. + * + * @return boolean Whether the cache is expired or not. + */ + public function is_expired(); + + /** + * Retrieves the timestamp of the cache. + * + * @return mixed Either the Unix time stamp of the cache creation, or boolean `false`. + */ + public function timestamp(); + + /** + * Resets the freshness of the cache. + * + * @return boolean Whether the operation was successful. + */ + public function reset(); +} diff --git a/3rdparty/aws-sdk/lib/dom/ArrayToDOMDocument.php b/3rdparty/aws-sdk/lib/dom/ArrayToDOMDocument.php new file mode 100644 index 0000000000..06ad502eeb --- /dev/null +++ b/3rdparty/aws-sdk/lib/dom/ArrayToDOMDocument.php @@ -0,0 +1,181 @@ +appendChild(self::createDOMElement($source, $rootTagName, $document)); + + return $document; + } + + /** + * @param array $source + * @param string $rootTagName + * @param bool $formatOutput + * @return string + */ + public static function arrayToXMLString(array $source, $rootTagName = 'root', $formatOutput = true) + { + $document = self::arrayToDOMDocument($source, $rootTagName); + $document->formatOutput = $formatOutput; + + return $document->saveXML(); + } + + /** + * @param DOMDocument $document + * @return array + */ + public static function domDocumentToArray(DOMDocument $document) + { + return self::createArray($document->documentElement); + } + + /** + * @param string $xmlString + * @return array + */ + public static function xmlStringToArray($xmlString) + { + $document = new DOMDocument(); + + return $document->loadXML($xmlString) ? self::domDocumentToArray($document) : array(); + } + + /** + * @param mixed $source + * @param string $tagName + * @param DOMDocument $document + * @return DOMNode + */ + private static function createDOMElement($source, $tagName, DOMDocument $document) + { + if (!is_array($source)) + { + $element = $document->createElement($tagName); + $element->appendChild($document->createCDATASection($source)); + + return $element; + } + + $element = $document->createElement($tagName); + + foreach ($source as $key => $value) + { + if (is_string($key) && !is_numeric($key)) + { + if ($key === self::ATTRIBUTES) + { + foreach ($value as $attributeName => $attributeValue) + { + $element->setAttribute($attributeName, $attributeValue); + } + } + elseif ($key === self::CONTENT) + { + $element->appendChild($document->createCDATASection($value)); + } + elseif (is_string($value) && !is_numeric($value)) + { + $element->appendChild(self::createDOMElement($value, $key, $document)); + } + elseif (is_array($value) && count($value)) + { + $keyNode = $document->createElement($key); + + foreach ($value as $elementKey => $elementValue) + { + if (is_string($elementKey) && !is_numeric($elementKey)) + { + $keyNode->appendChild(self::createDOMElement($elementValue, $elementKey, $document)); + } + else + { + $element->appendChild(self::createDOMElement($elementValue, $key, $document)); + } + } + + if ($keyNode->hasChildNodes()) + { + $element->appendChild($keyNode); + } + } + else + { + if (is_bool($value)) + { + $value = $value ? 'true' : 'false'; + } + + $element->appendChild(self::createDOMElement($value, $key, $document)); + } + } + else + { + $element->appendChild(self::createDOMElement($value, $tagName, $document)); + } + } + + return $element; + } + + /** + * @param DOMNode $domNode + * @return array + */ + private static function createArray(DOMNode $domNode) + { + $array = array(); + + for ($i = 0; $i < $domNode->childNodes->length; $i++) + { + $item = $domNode->childNodes->item($i); + + if ($item->nodeType === XML_ELEMENT_NODE) + { + $arrayElement = array(); + + for ($attributeIndex = 0; !is_null($attribute = $item->attributes->item($attributeIndex)); $attributeIndex++) + { + if ($attribute->nodeType === XML_ATTRIBUTE_NODE) + { + $arrayElement[self::ATTRIBUTES][$attribute->nodeName] = $attribute->nodeValue; + } + } + + $children = self::createArray($item); + + if (is_array($children)) + { + $arrayElement = array_merge($arrayElement, $children); + } + else + { + $arrayElement[self::CONTENT] = $children; + } + + $array[$item->nodeName][] = $arrayElement; + } + elseif ($item->nodeType === XML_CDATA_SECTION_NODE || ($item->nodeType === XML_TEXT_NODE && trim($item->nodeValue) !== '')) + { + return $item->nodeValue; + } + } + + return $array; + } +} diff --git a/3rdparty/aws-sdk/lib/requestcore/LICENSE b/3rdparty/aws-sdk/lib/requestcore/LICENSE new file mode 100755 index 0000000000..49b38bd620 --- /dev/null +++ b/3rdparty/aws-sdk/lib/requestcore/LICENSE @@ -0,0 +1,25 @@ +Copyright (c) 2006-2010 Ryan Parman, Foleeo Inc., and contributors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, this list + of conditions and the following disclaimer in the documentation and/or other materials + provided with the distribution. + + * Neither the name of Ryan Parman, Foleeo Inc. nor the names of its contributors may be used to + endorse or promote products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS +OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS +AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/3rdparty/aws-sdk/lib/requestcore/README.md b/3rdparty/aws-sdk/lib/requestcore/README.md new file mode 100755 index 0000000000..373ea4dcca --- /dev/null +++ b/3rdparty/aws-sdk/lib/requestcore/README.md @@ -0,0 +1,15 @@ +# RequestCore + +RequestCore is a lightweight cURL-based HTTP request/response class that leverages MultiCurl for parallel requests. + +### PEAR HTTP_Request? + +RequestCore was written as a replacement for [PEAR HTTP_Request](http://pear.php.net/http_request/). While PEAR HTTP_Request is full-featured and heavy, RequestCore features only the essentials and is very lightweight. It also leverages the batch request support in cURL's `curl_multi_exec()` to enable multi-threaded requests that fire in parallel. + +### Reference and Download + +You can find the class reference at . You can get the code from . + +### License and Copyright + +This code is Copyright (c) 2008-2010, Ryan Parman. However, I'm licensing this code for others to use under the [Simplified BSD license](http://www.opensource.org/licenses/bsd-license.php). diff --git a/3rdparty/aws-sdk/lib/requestcore/cacert.pem b/3rdparty/aws-sdk/lib/requestcore/cacert.pem new file mode 100755 index 0000000000..80bff62fd2 --- /dev/null +++ b/3rdparty/aws-sdk/lib/requestcore/cacert.pem @@ -0,0 +1,3390 @@ +## +## ca-bundle.crt -- Bundle of CA Root Certificates +## +## Certificate data from Mozilla as of: Wed Jan 18 00:04:16 2012 +## +## This is a bundle of X.509 certificates of public Certificate Authorities +## (CA). These were automatically extracted from Mozilla's root certificates +## file (certdata.txt). This file can be found in the mozilla source tree: +## http://mxr.mozilla.org/mozilla/source/security/nss/lib/ckfw/builtins/certdata.txt?raw=1 +## +## It contains the certificates in PEM format and therefore +## can be directly used with curl / libcurl / php_curl, or with +## an Apache+mod_ssl webserver for SSL client authentication. +## Just configure this file as the SSLCACertificateFile. +## + +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is the Netscape security libraries. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1994-2000 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** +# @(#) $RCSfile: certdata.txt,v $ $Revision: 1.81 $ $Date: 2012/01/17 22:02:37 $ + +GTE CyberTrust Global Root +========================== +-----BEGIN CERTIFICATE----- +MIICWjCCAcMCAgGlMA0GCSqGSIb3DQEBBAUAMHUxCzAJBgNVBAYTAlVTMRgwFgYDVQQKEw9HVEUg +Q29ycG9yYXRpb24xJzAlBgNVBAsTHkdURSBDeWJlclRydXN0IFNvbHV0aW9ucywgSW5jLjEjMCEG +A1UEAxMaR1RFIEN5YmVyVHJ1c3QgR2xvYmFsIFJvb3QwHhcNOTgwODEzMDAyOTAwWhcNMTgwODEz +MjM1OTAwWjB1MQswCQYDVQQGEwJVUzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQL +Ex5HVEUgQ3liZXJUcnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0 +IEdsb2JhbCBSb290MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCVD6C28FCc6HrHiM3dFw4u +sJTQGz0O9pTAipTHBsiQl8i4ZBp6fmw8U+E3KHNgf7KXUwefU/ltWJTSr41tiGeA5u2ylc9yMcql +HHK6XALnZELn+aks1joNrI1CqiQBOeacPwGFVw1Yh0X404Wqk2kmhXBIgD8SFcd5tB8FLztimQID +AQABMA0GCSqGSIb3DQEBBAUAA4GBAG3rGwnpXtlR22ciYaQqPEh346B8pt5zohQDhT37qw4wxYMW +M4ETCJ57NE7fQMh017l93PR2VX2bY1QY6fDq81yx2YtCHrnAlU66+tXifPVoYb+O7AWXX1uw16OF +NMQkpw0PlZPvy5TYnh+dXIVtx6quTx8itc2VrbqnzPmrC3p/ +-----END CERTIFICATE----- + +Thawte Server CA +================ +-----BEGIN CERTIFICATE----- +MIIDEzCCAnygAwIBAgIBATANBgkqhkiG9w0BAQQFADCBxDELMAkGA1UEBhMCWkExFTATBgNVBAgT +DFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29uc3Vs +dGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcGA1UE +AxMQVGhhd3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0ZS5j +b20wHhcNOTYwODAxMDAwMDAwWhcNMjAxMjMxMjM1OTU5WjCBxDELMAkGA1UEBhMCWkExFTATBgNV +BAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29u +c3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcG +A1UEAxMQVGhhd3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0 +ZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANOkUG7I/1Zr5s9dtuoMaHVHoqrC2oQl +/Kj0R1HahbUgdJSGHg91yekIYfUGbTBuFRkC6VLAYttNmZ7iagxEOM3+vuNkCXDF/rFrKbYvScg7 +1CcEJRCXL+eQbcAoQpnXTEPew/UhbVSfXcNY4cDk2VuwuNy0e982OsK1ZiIS1ocNAgMBAAGjEzAR +MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAB/pMaVz7lcxG7oWDTSEwjsrZqG9J +GubaUeNgcGyEYRGhGshIPllDfU+VPaGLtwtimHp1it2ITk6eQNuozDJ0uW8NxuOzRAvZim+aKZuZ +GCg70eNAKJpaPNW15yAbi8qkq43pUdniTCxZqdq5snUb9kLy78fyGPmJvKP/iiMucEc= +-----END CERTIFICATE----- + +Thawte Premium Server CA +======================== +-----BEGIN CERTIFICATE----- +MIIDJzCCApCgAwIBAgIBATANBgkqhkiG9w0BAQQFADCBzjELMAkGA1UEBhMCWkExFTATBgNVBAgT +DFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29uc3Vs +dGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UE +AxMYVGhhd3RlIFByZW1pdW0gU2VydmVyIENBMSgwJgYJKoZIhvcNAQkBFhlwcmVtaXVtLXNlcnZl +ckB0aGF3dGUuY29tMB4XDTk2MDgwMTAwMDAwMFoXDTIwMTIzMTIzNTk1OVowgc4xCzAJBgNVBAYT +AlpBMRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNVBAcTCUNhcGUgVG93bjEdMBsGA1UEChMU +VGhhd3RlIENvbnN1bHRpbmcgY2MxKDAmBgNVBAsTH0NlcnRpZmljYXRpb24gU2VydmljZXMgRGl2 +aXNpb24xITAfBgNVBAMTGFRoYXd0ZSBQcmVtaXVtIFNlcnZlciBDQTEoMCYGCSqGSIb3DQEJARYZ +cHJlbWl1bS1zZXJ2ZXJAdGhhd3RlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA0jY2 +aovXwlue2oFBYo847kkEVdbQ7xwblRZH7xhINTpS9CtqBo87L+pW46+GjZ4X9560ZXUCTe/LCaIh +Udib0GfQug2SBhRz1JPLlyoAnFxODLz6FVL88kRu2hFKbgifLy3j+ao6hnO2RlNYyIkFvYMRuHM/ +qgeN9EJN50CdHDcCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQQFAAOBgQAm +SCwWwlj66BZ0DKqqX1Q/8tfJeGBeXm43YyJ3Nn6yF8Q0ufUIhfzJATj/Tb7yFkJD57taRvvBxhEf +8UqwKEbJw8RCfbz6q1lu1bdRiBHjpIUZa4JMpAwSremkrj/xw0llmozFyD4lt5SZu5IycQfwhl7t +UCemDaYj+bvLpgcUQg== +-----END CERTIFICATE----- + +Equifax Secure CA +================= +-----BEGIN CERTIFICATE----- +MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJVUzEQMA4GA1UE +ChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5 +MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoT +B0VxdWlmYXgxLTArBgNVBAsTJEVxdWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCB +nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPR +fM6fBeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+AcJkVV5MW +8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kCAwEAAaOCAQkwggEFMHAG +A1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UE +CxMkRXF1aWZheCBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoG +A1UdEAQTMBGBDzIwMTgwODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvS +spXXR9gjIBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQFMAMB +Af8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUAA4GBAFjOKer89961 +zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y7qj/WsjTVbJmcVfewCHrPSqnI0kB +BIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee95 +70+sB3c4 +-----END CERTIFICATE----- + +Digital Signature Trust Co. Global CA 1 +======================================= +-----BEGIN CERTIFICATE----- +MIIDKTCCApKgAwIBAgIENnAVljANBgkqhkiG9w0BAQUFADBGMQswCQYDVQQGEwJVUzEkMCIGA1UE +ChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMREwDwYDVQQLEwhEU1RDQSBFMTAeFw05ODEy +MTAxODEwMjNaFw0xODEyMTAxODQwMjNaMEYxCzAJBgNVBAYTAlVTMSQwIgYDVQQKExtEaWdpdGFs +IFNpZ25hdHVyZSBUcnVzdCBDby4xETAPBgNVBAsTCERTVENBIEUxMIGdMA0GCSqGSIb3DQEBAQUA +A4GLADCBhwKBgQCgbIGpzzQeJN3+hijM3oMv+V7UQtLodGBmE5gGHKlREmlvMVW5SXIACH7TpWJE +NySZj9mDSI+ZbZUTu0M7LklOiDfBu1h//uG9+LthzfNHwJmm8fOR6Hh8AMthyUQncWlVSn5JTe2i +o74CTADKAqjuAQIxZA9SLRN0dja1erQtcQIBA6OCASQwggEgMBEGCWCGSAGG+EIBAQQEAwIABzBo +BgNVHR8EYTBfMF2gW6BZpFcwVTELMAkGA1UEBhMCVVMxJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0 +dXJlIFRydXN0IENvLjERMA8GA1UECxMIRFNUQ0EgRTExDTALBgNVBAMTBENSTDEwKwYDVR0QBCQw +IoAPMTk5ODEyMTAxODEwMjNagQ8yMDE4MTIxMDE4MTAyM1owCwYDVR0PBAQDAgEGMB8GA1UdIwQY +MBaAFGp5fpFpRhgTCgJ3pVlbYJglDqL4MB0GA1UdDgQWBBRqeX6RaUYYEwoCd6VZW2CYJQ6i+DAM +BgNVHRMEBTADAQH/MBkGCSqGSIb2fQdBAAQMMAobBFY0LjADAgSQMA0GCSqGSIb3DQEBBQUAA4GB +ACIS2Hod3IEGtgllsofIH160L+nEHvI8wbsEkBFKg05+k7lNQseSJqBcNJo4cvj9axY+IO6CizEq +kzaFI4iKPANo08kJD038bKTaKHKTDomAsH3+gG9lbRgzl4vCa4nuYD3Im+9/KzJic5PLPON74nZ4 +RbyhkwS7hp86W0N6w4pl +-----END CERTIFICATE----- + +Digital Signature Trust Co. Global CA 3 +======================================= +-----BEGIN CERTIFICATE----- +MIIDKTCCApKgAwIBAgIENm7TzjANBgkqhkiG9w0BAQUFADBGMQswCQYDVQQGEwJVUzEkMCIGA1UE +ChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMREwDwYDVQQLEwhEU1RDQSBFMjAeFw05ODEy +MDkxOTE3MjZaFw0xODEyMDkxOTQ3MjZaMEYxCzAJBgNVBAYTAlVTMSQwIgYDVQQKExtEaWdpdGFs +IFNpZ25hdHVyZSBUcnVzdCBDby4xETAPBgNVBAsTCERTVENBIEUyMIGdMA0GCSqGSIb3DQEBAQUA +A4GLADCBhwKBgQC/k48Xku8zExjrEH9OFr//Bo8qhbxe+SSmJIi2A7fBw18DW9Fvrn5C6mYjuGOD +VvsoLeE4i7TuqAHhzhy2iCoiRoX7n6dwqUcUP87eZfCocfdPJmyMvMa1795JJ/9IKn3oTQPMx7JS +xhcxEzu1TdvIxPbDDyQq2gyd55FbgM2UnQIBA6OCASQwggEgMBEGCWCGSAGG+EIBAQQEAwIABzBo +BgNVHR8EYTBfMF2gW6BZpFcwVTELMAkGA1UEBhMCVVMxJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0 +dXJlIFRydXN0IENvLjERMA8GA1UECxMIRFNUQ0EgRTIxDTALBgNVBAMTBENSTDEwKwYDVR0QBCQw +IoAPMTk5ODEyMDkxOTE3MjZagQ8yMDE4MTIwOTE5MTcyNlowCwYDVR0PBAQDAgEGMB8GA1UdIwQY +MBaAFB6CTShlgDzJQW6sNS5ay97u+DlbMB0GA1UdDgQWBBQegk0oZYA8yUFurDUuWsve7vg5WzAM +BgNVHRMEBTADAQH/MBkGCSqGSIb2fQdBAAQMMAobBFY0LjADAgSQMA0GCSqGSIb3DQEBBQUAA4GB +AEeNg61i8tuwnkUiBbmi1gMOOHLnnvx75pO2mqWilMg0HZHRxdf0CiUPPXiBng+xZ8SQTGPdXqfi +up/1902lMXucKS1M/mQ+7LZT/uqb7YLbdHVLB3luHtgZg3Pe9T7Qtd7nS2h9Qy4qIOF+oHhEngj1 +mPnHfxsb1gYgAlihw6ID +-----END CERTIFICATE----- + +Verisign Class 3 Public Primary Certification Authority +======================================================= +-----BEGIN CERTIFICATE----- +MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzELMAkGA1UEBhMCVVMx +FzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmltYXJ5 +IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVow +XzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAz +IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUA +A4GNADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhEBarsAx94 +f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/isI19wKTakyYbnsZogy1Ol +hec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0GCSqGSIb3DQEBAgUAA4GBALtMEivPLCYA +TxQT3ab7/AoRhIzzKBxnki98tsX63/Dolbwdj2wsqFHMc9ikwFPwTtYmwHYBV4GSXiHx0bH/59Ah +WM1pF+NEHJwZRDmJXNycAA9WjQKZ7aKQRUzkuxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2Omuf +Tqj/ZA1k +-----END CERTIFICATE----- + +Verisign Class 3 Public Primary Certification Authority - G2 +============================================================ +-----BEGIN CERTIFICATE----- +MIIDAjCCAmsCEH3Z/gfPqB63EHln+6eJNMYwDQYJKoZIhvcNAQEFBQAwgcExCzAJBgNVBAYTAlVT +MRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMgUHJpbWFy +eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2ln +biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz +dCBOZXR3b3JrMB4XDTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVT +MRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMgUHJpbWFy +eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2ln +biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz +dCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMXtERXVxp0KvTuWpMmR9ZmDCO +FoUgRm1HP9SFIIThbbP4pO0M8RcPO/mn+SXXwc+EY/J8Y8+iR/LGWzOOZEAEaMGAuWQcRXfH2G71 +lSk8UOg013gfqLptQ5GVj0VXXn7F+8qkBOvqlzdUMG+7AUcyM83cV5tkaWH4mx0ciU9cZwIDAQAB +MA0GCSqGSIb3DQEBBQUAA4GBAFFNzb5cy5gZnBWyATl4Lk0PZ3BwmcYQWpSkU01UbSuvDV1Ai2TT +1+7eVmGSX6bEHRBhNtMsJzzoKQm5EWR0zLVznxxIqbxhAe7iF6YM40AIOw7n60RzKprxaZLvcRTD +Oaxxp5EJb+RxBrO6WVcmeQD2+A2iMzAo1KpYoJ2daZH9 +-----END CERTIFICATE----- + +GlobalSign Root CA +================== +-----BEGIN CERTIFICATE----- +MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkGA1UEBhMCQkUx +GTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jvb3QgQ0ExGzAZBgNVBAMTEkds +b2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAwMDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNV +BAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYD +VQQDExJHbG9iYWxTaWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDa +DuaZjc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavpxy0Sy6sc +THAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp1Wrjsok6Vjk4bwY8iGlb +Kk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdGsnUOhugZitVtbNV4FpWi6cgKOOvyJBNP +c1STE4U6G7weNLWLBYy5d4ux2x8gkasJU26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrX +gzT/LCrBbBlDSgeF59N89iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0BAQUF +AAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOzyj1hTdNGCbM+w6Dj +Y1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE38NflNUVyRRBnMRddWQVDf9VMOyG +j/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymPAbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhH +hm4qxFYxldBniYUr+WymXUadDKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveC +X4XSQRjbgbMEHMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A== +-----END CERTIFICATE----- + +GlobalSign Root CA - R2 +======================= +-----BEGIN CERTIFICATE----- +MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4GA1UECxMXR2xv +YmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2Jh +bFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxT +aWduIFJvb3QgQ0EgLSBSMjETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2ln +bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6 +ErPLv4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8eoLrvozp +s6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklqtTleiDTsvHgMCJiEbKjN +S7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzdC9XZzPnqJworc5HGnRusyMvo4KD0L5CL +TfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pazq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6C +ygPCm48CAwEAAaOBnDCBmTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E +FgQUm+IHV2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5nbG9i +YWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG3lm0mi3f3BmGLjAN +BgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4GsJ0/WwbgcQ3izDJr86iw8bmEbTUsp +9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu +01yiPqFbQfXf5WRDLenVOavSot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG7 +9G+dwfCMNYxdAfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7 +TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg== +-----END CERTIFICATE----- + +ValiCert Class 1 VA +=================== +-----BEGIN CERTIFICATE----- +MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRp +b24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs +YXNzIDEgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZh +bGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNTIy +MjM0OFoXDTE5MDYyNTIyMjM0OFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0 +d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDEg +UG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0 +LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3DQEBAQUA +A4GNADCBiQKBgQDYWYJ6ibiWuqYvaG9YLqdUHAZu9OqNSLwxlBfw8068srg1knaw0KWlAdcAAxIi +GQj4/xEjm84H9b9pGib+TunRf50sQB1ZaG6m+FiwnRqP0z/x3BkGgagO4DrdyFNFCQbmD3DD+kCm +DuJWBQ8YTfwggtFzVXSNdnKgHZ0dwN0/cQIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFBoPUn0LBwG +lN+VYH+Wexf+T3GtZMjdd9LvWVXoP+iOBSoh8gfStadS/pyxtuJbdxdA6nLWI8sogTLDAHkY7FkX +icnGah5xyf23dKUlRWnFSKsZ4UWKJWsZ7uW7EvV/96aNUcPwnXS3qT6gpf+2SQMT2iLM7XGCK5nP +Orf1LXLI +-----END CERTIFICATE----- + +ValiCert Class 2 VA +=================== +-----BEGIN CERTIFICATE----- +MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRp +b24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs +YXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZh +bGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAw +MTk1NFoXDTE5MDYyNjAwMTk1NFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0 +d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIg +UG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0 +LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3DQEBAQUA +A4GNADCBiQKBgQDOOnHK5avIWZJV16vYdA757tn2VUdZZUcOBVXc65g2PFxTXdMwzzjsvUGJ7SVC +CSRrCl6zfN1SLUzm1NZ9WlmpZdRJEy0kTRxQb7XBhVQ7/nHk01xC+YDgkRoKWzk2Z/M/VXwbP7Rf +ZHM047QSv4dk+NoS/zcnwbNDu+97bi5p9wIDAQABMA0GCSqGSIb3DQEBBQUAA4GBADt/UG9vUJSZ +SWI4OB9L+KXIPqeCgfYrx+jFzug6EILLGACOTb2oWH+heQC1u+mNr0HZDzTuIYEZoDJJKPTEjlbV +UjP9UNV+mWwD5MlM/Mtsq2azSiGM5bUMMj4QssxsodyamEwCW/POuZ6lcg5Ktz885hZo+L7tdEy8 +W9ViH0Pd +-----END CERTIFICATE----- + +RSA Root Certificate 1 +====================== +-----BEGIN CERTIFICATE----- +MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRp +b24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs +YXNzIDMgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZh +bGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAw +MjIzM1oXDTE5MDYyNjAwMjIzM1owgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0 +d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDMg +UG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0 +LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3DQEBAQUA +A4GNADCBiQKBgQDjmFGWHOjVsQaBalfDcnWTq8+epvzzFlLWLU2fNUSoLgRNB0mKOCn1dzfnt6td +3zZxFJmP3MKS8edgkpfs2Ejcv8ECIMYkpChMMFp2bbFc893enhBxoYjHW5tBbcqwuI4V7q0zK89H +BFx1cQqYJJgpp0lZpd34t0NiYfPT4tBVPwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFa7AliEZwgs +3x/be0kz9dNnnfS0ChCzycUs4pJqcXgn8nCDQtM+z6lU9PHYkhaM0QTLS6vJn0WuPIqpsHEzXcjF +V9+vqDWzf4mH6eglkrh/hXqu1rweN1gqZ8mRzyqBPu3GOd/APhmcGcwTTYJBtYze4D1gCCAPRX5r +on+jjBXu +-----END CERTIFICATE----- + +Verisign Class 3 Public Primary Certification Authority - G3 +============================================================ +-----BEGIN CERTIFICATE----- +MIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHKMQswCQYDVQQGEwJV +UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv +cmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl +IG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQsw +CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRy +dXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhv +cml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkg +Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAMu6nFL8eB8aHm8bN3O9+MlrlBIwT/A2R/XQkQr1F8ilYcEWQE37imGQ5XYgwREGfassbqb1 +EUGO+i2tKmFZpGcmTNDovFJbcCAEWNF6yaRpvIMXZK0Fi7zQWM6NjPXr8EJJC52XJ2cybuGukxUc +cLwgTS8Y3pKI6GyFVxEa6X7jJhFUokWWVYPKMIno3Nij7SqAP395ZVc+FSBmCC+Vk7+qRy+oRpfw +EuL+wgorUeZ25rdGt+INpsyow0xZVYnm6FNcHOqd8GIWC6fJXwzw3sJ2zq/3avL6QaaiMxTJ5Xpj +055iN9WFZZ4O5lMkdBteHRJTW8cs54NJOxWuimi5V5cCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA +ERSWwauSCPc/L8my/uRan2Te2yFPhpk0djZX3dAVL8WtfxUfN2JzPtTnX84XA9s1+ivbrmAJXx5f +j267Cz3qWhMeDGBvtcC1IyIuBwvLqXTLR7sdwdela8wv0kL9Sd2nic9TutoAWii/gt/4uhMdUIaC +/Y4wjylGsB49Ndo4YhYYSq3mtlFs3q9i6wHQHiT+eo8SGhJouPtmmRQURVyu565pF4ErWjfJXir0 +xuKhXFSbplQAz/DxwceYMBo7Nhbbo27q/a2ywtrvAkcTisDxszGtTxzhT5yvDwyd93gN2PQ1VoDa +t20Xj50egWTh/sVFuq1ruQp6Tk9LhO5L8X3dEQ== +-----END CERTIFICATE----- + +Verisign Class 4 Public Primary Certification Authority - G3 +============================================================ +-----BEGIN CERTIFICATE----- +MIIEGjCCAwICEQDsoKeLbnVqAc/EfMwvlF7XMA0GCSqGSIb3DQEBBQUAMIHKMQswCQYDVQQGEwJV +UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv +cmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl +IG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDQgUHVibGljIFByaW1hcnkgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQsw +CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRy +dXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhv +cml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDQgUHVibGljIFByaW1hcnkg +Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAK3LpRFpxlmr8Y+1GQ9Wzsy1HyDkniYlS+BzZYlZ3tCD5PUPtbut8XzoIfzk6AzufEUiGXaS +tBO3IFsJ+mGuqPKljYXCKtbeZjbSmwL0qJJgfJxptI8kHtCGUvYynEFYHiK9zUVilQhu0GbdU6LM +8BDcVHOLBKFGMzNcF0C5nk3T875Vg+ixiY5afJqWIpA7iCXy0lOIAgwLePLmNxdLMEYH5IBtptiW +Lugs+BGzOA1mppvqySNb247i8xOOGlktqgLw7KSHZtzBP/XYufTsgsbSPZUd5cBPhMnZo0QoBmrX +Razwa2rvTl/4EYIeOGM0ZlDUPpNz+jDDZq3/ky2X7wMCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA +j/ola09b5KROJ1WrIhVZPMq1CtRK26vdoV9TxaBXOcLORyu+OshWv8LZJxA6sQU8wHcxuzrTBXtt +mhwwjIDLk5Mqg6sFUYICABFna/OIYUdfA5PVWw3g8dShMjWFsjrbsIKr0csKvE+MW8VLADsfKoKm +fjaF3H48ZwC15DtS4KjrXRX5xm3wrR0OhbepmnMUWluPQSjA1egtTaRezarZ7c7c2NU8Qh0XwRJd +RTjDOPP8hS6DRkiy1yBfkjaP53kPmF6Z6PDQpLv1U70qzlmwr25/bLvSHgCwIe34QWKCudiyxLtG +UPMxxY8BqHTr9Xgn2uf3ZkPznoM+IKrDNWCRzg== +-----END CERTIFICATE----- + +Entrust.net Secure Server CA +============================ +-----BEGIN CERTIFICATE----- +MIIE2DCCBEGgAwIBAgIEN0rSQzANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMCVVMxFDASBgNV +BAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5uZXQvQ1BTIGluY29ycC4gYnkg +cmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRl +ZDE6MDgGA1UEAxMxRW50cnVzdC5uZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhv +cml0eTAeFw05OTA1MjUxNjA5NDBaFw0xOTA1MjUxNjM5NDBaMIHDMQswCQYDVQQGEwJVUzEUMBIG +A1UEChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5jb3JwLiBi +eSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBFbnRydXN0Lm5ldCBMaW1p +dGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQDNKIM0VBuJ8w+vN5Ex/68xYMmo6LIQ +aO2f55M28Qpku0f1BBc/I0dNxScZgSYMVHINiC3ZH5oSn7yzcdOAGT9HZnuMNSjSuQrfJNqc1lB5 +gXpa0zf3wkrYKZImZNHkmGw6AIr1NJtl+O3jEP/9uElY3KDegjlrgbEWGWG5VLbmQwIBA6OCAdcw +ggHTMBEGCWCGSAGG+EIBAQQEAwIABzCCARkGA1UdHwSCARAwggEMMIHeoIHboIHYpIHVMIHSMQsw +CQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5l +dC9DUFMgaW5jb3JwLiBieSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBF +bnRydXN0Lm5ldCBMaW1pdGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENl +cnRpZmljYXRpb24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMCmgJ6AlhiNodHRwOi8vd3d3LmVu +dHJ1c3QubmV0L0NSTC9uZXQxLmNybDArBgNVHRAEJDAigA8xOTk5MDUyNTE2MDk0MFqBDzIwMTkw +NTI1MTYwOTQwWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAU8BdiE1U9s/8KAGv7UISX8+1i0Bow +HQYDVR0OBBYEFPAXYhNVPbP/CgBr+1CEl/PtYtAaMAwGA1UdEwQFMAMBAf8wGQYJKoZIhvZ9B0EA +BAwwChsEVjQuMAMCBJAwDQYJKoZIhvcNAQEFBQADgYEAkNwwAvpkdMKnCqV8IY00F6j7Rw7/JXyN +Ewr75Ji174z4xRAN95K+8cPV1ZVqBLssziY2ZcgxxufuP+NXdYR6Ee9GTxj005i7qIcyunL2POI9 +n9cd2cNgQ4xYDiKWL2KjLB+6rQXvqzJ4h6BUcxm1XAX5Uj5tLUUL9wqT6u0G+bI= +-----END CERTIFICATE----- + +Entrust.net Premium 2048 Secure Server CA +========================================= +-----BEGIN CERTIFICATE----- +MIIEXDCCA0SgAwIBAgIEOGO5ZjANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChMLRW50cnVzdC5u +ZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBpbmNvcnAuIGJ5IHJlZi4gKGxp +bWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNV +BAMTKkVudHJ1c3QubmV0IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQx +NzUwNTFaFw0xOTEyMjQxODIwNTFaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3 +d3d3LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTEl +MCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5u +ZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgpMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEArU1LqRKGsuqjIAcVFmQqK0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOL +Gp18EzoOH1u3Hs/lJBQesYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSr +hRSGlVuXMlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVTXTzW +nLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/HoZdenoVve8AjhUi +VBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH4QIDAQABo3QwcjARBglghkgBhvhC +AQEEBAMCAAcwHwYDVR0jBBgwFoAUVeSB0RGAvtiJuQijMfmhJAkWuXAwHQYDVR0OBBYEFFXkgdER +gL7YibkIozH5oSQJFrlwMB0GCSqGSIb2fQdBAAQQMA4bCFY1LjA6NC4wAwIEkDANBgkqhkiG9w0B +AQUFAAOCAQEAWUesIYSKF8mciVMeuoCFGsY8Tj6xnLZ8xpJdGGQC49MGCBFhfGPjK50xA3B20qMo +oPS7mmNz7W3lKtvtFKkrxjYR0CvrB4ul2p5cGZ1WEvVUKcgF7bISKo30Axv/55IQh7A6tcOdBTcS +o8f0FbnVpDkWm1M6I5HxqIKiaohowXkCIryqptau37AUX7iH0N18f3v/rxzP5tsHrV7bhZ3QKw0z +2wTR5klAEyt2+z7pnIkPFc4YsIV4IU9rTw76NmfNB/L/CNDi3tm/Kq+4h4YhPATKt5Rof8886ZjX +OP/swNlQ8C5LWK5Gb9Auw2DaclVyvUxFnmG6v4SBkgPR0ml8xQ== +-----END CERTIFICATE----- + +Baltimore CyberTrust Root +========================= +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJRTESMBAGA1UE +ChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYDVQQDExlCYWx0aW1vcmUgQ3li +ZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoXDTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMC +SUUxEjAQBgNVBAoTCUJhbHRpbW9yZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFs +dGltb3JlIEN5YmVyVHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKME +uyKrmD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjrIZ3AQSsB +UnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeKmpYcqWe4PwzV9/lSEy/C +G9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSuXmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9 +XbIGevOF6uvUA65ehD5f/xXtabz5OTZydc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjpr +l3RjM71oGDHweI12v/yejl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoI +VDaGezq1BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEB +BQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT929hkTI7gQCvlYpNRh +cL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3WgxjkzSswF07r51XgdIGn9w/xZchMB5 +hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsa +Y71k5h+3zvDyny67G7fyUIhzksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9H +RCwBXbsdtTLSR9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp +-----END CERTIFICATE----- + +Equifax Secure Global eBusiness CA +================================== +-----BEGIN CERTIFICATE----- +MIICkDCCAfmgAwIBAgIBATANBgkqhkiG9w0BAQQFADBaMQswCQYDVQQGEwJVUzEcMBoGA1UEChMT +RXF1aWZheCBTZWN1cmUgSW5jLjEtMCsGA1UEAxMkRXF1aWZheCBTZWN1cmUgR2xvYmFsIGVCdXNp +bmVzcyBDQS0xMB4XDTk5MDYyMTA0MDAwMFoXDTIwMDYyMTA0MDAwMFowWjELMAkGA1UEBhMCVVMx +HDAaBgNVBAoTE0VxdWlmYXggU2VjdXJlIEluYy4xLTArBgNVBAMTJEVxdWlmYXggU2VjdXJlIEds +b2JhbCBlQnVzaW5lc3MgQ0EtMTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAuucXkAJlsTRV +PEnCUdXfp9E3j9HngXNBUmCbnaEXJnitx7HoJpQytd4zjTov2/KaelpzmKNc6fuKcxtc58O/gGzN +qfTWK8D3+ZmqY6KxRwIP1ORROhI8bIpaVIRw28HFkM9yRcuoWcDNM50/o5brhTMhHD4ePmBudpxn +hcXIw2ECAwEAAaNmMGQwEQYJYIZIAYb4QgEBBAQDAgAHMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0j +BBgwFoAUvqigdHJQa0S3ySPY+6j/s1draGwwHQYDVR0OBBYEFL6ooHRyUGtEt8kj2Puo/7NXa2hs +MA0GCSqGSIb3DQEBBAUAA4GBADDiAVGqx+pf2rnQZQ8w1j7aDRRJbpGTJxQx78T3LUX47Me/okEN +I7SS+RkAZ70Br83gcfxaz2TE4JaY0KNA4gGK7ycH8WUBikQtBmV1UsCGECAhX2xrD2yuCRyv8qIY +NMR1pHMc8Y3c7635s3a0kr/clRAevsvIO1qEYBlWlKlV +-----END CERTIFICATE----- + +Equifax Secure eBusiness CA 1 +============================= +-----BEGIN CERTIFICATE----- +MIICgjCCAeugAwIBAgIBBDANBgkqhkiG9w0BAQQFADBTMQswCQYDVQQGEwJVUzEcMBoGA1UEChMT +RXF1aWZheCBTZWN1cmUgSW5jLjEmMCQGA1UEAxMdRXF1aWZheCBTZWN1cmUgZUJ1c2luZXNzIENB +LTEwHhcNOTkwNjIxMDQwMDAwWhcNMjAwNjIxMDQwMDAwWjBTMQswCQYDVQQGEwJVUzEcMBoGA1UE +ChMTRXF1aWZheCBTZWN1cmUgSW5jLjEmMCQGA1UEAxMdRXF1aWZheCBTZWN1cmUgZUJ1c2luZXNz +IENBLTEwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAM4vGbwXt3fek6lfWg0XTzQaDJj0ItlZ +1MRoRvC0NcWFAyDGr0WlIVFFQesWWDYyb+JQYmT5/VGcqiTZ9J2DKocKIdMSODRsjQBuWqDZQu4a +IZX5UkxVWsUPOE9G+m34LjXWHXzr4vCwdYDIqROsvojvOm6rXyo4YgKwEnv+j6YDAgMBAAGjZjBk +MBEGCWCGSAGG+EIBAQQEAwIABzAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFEp4MlIR21kW +Nl7fwRQ2QGpHfEyhMB0GA1UdDgQWBBRKeDJSEdtZFjZe38EUNkBqR3xMoTANBgkqhkiG9w0BAQQF +AAOBgQB1W6ibAxHm6VZMzfmpTMANmvPMZWnmJXbMWbfWVMMdzZmsGd20hdXgPfxiIKeES1hl8eL5 +lSE/9dR+WB5Hh1Q+WKG1tfgq73HnvMP2sUlG4tega+VWeponmHxGYhTnyfxuAxJ5gDgdSIKN/Bf+ +KpYrtWKmpj29f5JZzVoqgrI3eQ== +-----END CERTIFICATE----- + +Equifax Secure eBusiness CA 2 +============================= +-----BEGIN CERTIFICATE----- +MIIDIDCCAomgAwIBAgIEN3DPtTANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJVUzEXMBUGA1UE +ChMORXF1aWZheCBTZWN1cmUxJjAkBgNVBAsTHUVxdWlmYXggU2VjdXJlIGVCdXNpbmVzcyBDQS0y +MB4XDTk5MDYyMzEyMTQ0NVoXDTE5MDYyMzEyMTQ0NVowTjELMAkGA1UEBhMCVVMxFzAVBgNVBAoT +DkVxdWlmYXggU2VjdXJlMSYwJAYDVQQLEx1FcXVpZmF4IFNlY3VyZSBlQnVzaW5lc3MgQ0EtMjCB +nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA5Dk5kx5SBhsoNviyoynF7Y6yEb3+6+e0dMKP/wXn +2Z0GvxLIPw7y1tEkshHe0XMJitSxLJgJDR5QRrKDpkWNYmi7hRsgcDKqQM2mll/EcTc/BPO3QSQ5 +BxoeLmFYoBIL5aXfxavqN3HMHMg3OrmXUqesxWoklE6ce8/AatbfIb0CAwEAAaOCAQkwggEFMHAG +A1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORXF1aWZheCBTZWN1cmUx +JjAkBgNVBAsTHUVxdWlmYXggU2VjdXJlIGVCdXNpbmVzcyBDQS0yMQ0wCwYDVQQDEwRDUkwxMBoG +A1UdEAQTMBGBDzIwMTkwNjIzMTIxNDQ1WjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUUJ4L6q9e +uSBIplBqy/3YIHqngnYwHQYDVR0OBBYEFFCeC+qvXrkgSKZQasv92CB6p4J2MAwGA1UdEwQFMAMB +Af8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUAA4GBAAyGgq3oThr1 +jokn4jVYPSm0B482UJW/bsGe68SQsoWou7dC4A8HOd/7npCy0cE+U58DRLB+S/Rv5Hwf5+Kx5Lia +78O9zt4LMjTZ3ijtM2vE1Nc9ElirfQkty3D1E4qUoSek1nDFbZS1yX2doNLGCEnZZpum0/QL3MUm +V+GRMOrN +-----END CERTIFICATE----- + +AddTrust Low-Value Services Root +================================ +-----BEGIN CERTIFICATE----- +MIIEGDCCAwCgAwIBAgIBATANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQGEwJTRTEUMBIGA1UEChML +QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYDVQQDExhBZGRU +cnVzdCBDbGFzcyAxIENBIFJvb3QwHhcNMDAwNTMwMTAzODMxWhcNMjAwNTMwMTAzODMxWjBlMQsw +CQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBO +ZXR3b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3QwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQCWltQhSWDia+hBBwzexODcEyPNwTXH+9ZOEQpnXvUGW2ulCDtbKRY6 +54eyNAbFvAWlA3yCyykQruGIgb3WntP+LVbBFc7jJp0VLhD7Bo8wBN6ntGO0/7Gcrjyvd7ZWxbWr +oulpOj0OM3kyP3CCkplhbY0wCI9xP6ZIVxn4JdxLZlyldI+Yrsj5wAYi56xz36Uu+1LcsRVlIPo1 +Zmne3yzxbrww2ywkEtvrNTVokMsAsJchPXQhI2U0K7t4WaPW4XY5mqRJjox0r26kmqPZm9I4XJui +GMx1I4S+6+JNM3GOGvDC+Mcdoq0Dlyz4zyXG9rgkMbFjXZJ/Y/AlyVMuH79NAgMBAAGjgdIwgc8w +HQYDVR0OBBYEFJWxtPCUtr3H2tERCSG+wa9J/RB7MAsGA1UdDwQEAwIBBjAPBgNVHRMBAf8EBTAD +AQH/MIGPBgNVHSMEgYcwgYSAFJWxtPCUtr3H2tERCSG+wa9J/RB7oWmkZzBlMQswCQYDVQQGEwJT +RTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSEw +HwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBACxt +ZBsfzQ3duQH6lmM0MkhHma6X7f1yFqZzR1r0693p9db7RcwpiURdv0Y5PejuvE1Uhh4dbOMXJ0Ph +iVYrqW9yTkkz43J8KiOavD7/KCrto/8cI7pDVwlnTUtiBi34/2ydYB7YHEt9tTEv2dB8Xfjea4MY +eDdXL+gzB2ffHsdrKpV2ro9Xo/D0UrSpUwjP4E/TelOL/bscVjby/rK25Xa71SJlpz/+0WatC7xr +mYbvP33zGDLKe8bjq2RGlfgmadlVg3sslgf/WSxEo8bl6ancoWOAWiFeIc9TVPC6b4nbqKqVz4vj +ccweGyBECMB6tkD9xOQ14R0WHNC8K47Wcdk= +-----END CERTIFICATE----- + +AddTrust External Root +====================== +-----BEGIN CERTIFICATE----- +MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEUMBIGA1UEChML +QWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYD +VQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEw +NDgzOFowbzELMAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRU +cnVzdCBFeHRlcm5hbCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0Eg +Um9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvtH7xsD821 ++iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9uMq/NzgtHj6RQa1wVsfw +Tz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzXmk6vBbOmcZSccbNQYArHE504B4YCqOmo +aSYYkKtMsE8jqzpPhNjfzp/haW+710LXa0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy +2xSoRcRdKn23tNbE7qzNE0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv7 +7+ldU9U0WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYDVR0P +BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0Jvf6xCZU7wO94CTL +VBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEmMCQGA1UECxMdQWRk +VHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsxIjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENB +IFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZl +j7DYd7usQWxHYINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5 +6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvCNr4TDea9Y355 +e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEXc4g/VhsxOBi0cQ+azcgOno4u +G+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5amnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ= +-----END CERTIFICATE----- + +AddTrust Public Services Root +============================= +-----BEGIN CERTIFICATE----- +MIIEFTCCAv2gAwIBAgIBATANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJTRTEUMBIGA1UEChML +QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSAwHgYDVQQDExdBZGRU +cnVzdCBQdWJsaWMgQ0EgUm9vdDAeFw0wMDA1MzAxMDQxNTBaFw0yMDA1MzAxMDQxNTBaMGQxCzAJ +BgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQIE5l +dHdvcmsxIDAeBgNVBAMTF0FkZFRydXN0IFB1YmxpYyBDQSBSb290MIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEA6Rowj4OIFMEg2Dybjxt+A3S72mnTRqX4jsIMEZBRpS9mVEBV6tsfSlbu +nyNu9DnLoblv8n75XYcmYZ4c+OLspoH4IcUkzBEMP9smcnrHAZcHF/nXGCwwfQ56HmIexkvA/X1i +d9NEHif2P0tEs7c42TkfYNVRknMDtABp4/MUTu7R3AnPdzRGULD4EfL+OHn3Bzn+UZKXC1sIXzSG +Aa2Il+tmzV7R/9x98oTaunet3IAIx6eH1lWfl2royBFkuucZKT8Rs3iQhCBSWxHveNCD9tVIkNAw +HM+A+WD+eeSI8t0A65RF62WUaUC6wNW0uLp9BBGo6zEFlpROWCGOn9Bg/QIDAQABo4HRMIHOMB0G +A1UdDgQWBBSBPjfYkrAfd59ctKtzquf2NGAv+jALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zCBjgYDVR0jBIGGMIGDgBSBPjfYkrAfd59ctKtzquf2NGAv+qFopGYwZDELMAkGA1UEBhMCU0Ux +FDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQLExRBZGRUcnVzdCBUVFAgTmV0d29yazEgMB4G +A1UEAxMXQWRkVHJ1c3QgUHVibGljIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBAAP3FUr4 +JNojVhaTdt02KLmuG7jD8WS6IBh4lSknVwW8fCr0uVFV2ocC3g8WFzH4qnkuCRO7r7IgGRLlk/lL ++YPoRNWyQSW/iHVv/xD8SlTQX/D67zZzfRs2RcYhbbQVuE7PnFylPVoAjgbjPGsye/Kf8Lb93/Ao +GEjwxrzQvzSAlsJKsW2Ox5BF3i9nrEUEo3rcVZLJR2bYGozH7ZxOmuASu7VqTITh4SINhwBk/ox9 +Yjllpu9CtoAlEmEBqCQTcAARJl/6NVDFSMwGR+gn2HCNX2TmoUQmXiLsks3/QppEIW1cxeMiHV9H +EufOX1362KqxMy3ZdvJOOjMMK7MtkAY= +-----END CERTIFICATE----- + +AddTrust Qualified Certificates Root +==================================== +-----BEGIN CERTIFICATE----- +MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJTRTEUMBIGA1UEChML +QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSMwIQYDVQQDExpBZGRU +cnVzdCBRdWFsaWZpZWQgQ0EgUm9vdDAeFw0wMDA1MzAxMDQ0NTBaFw0yMDA1MzAxMDQ0NTBaMGcx +CzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQ +IE5ldHdvcmsxIzAhBgNVBAMTGkFkZFRydXN0IFF1YWxpZmllZCBDQSBSb290MIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5B6a/twJWoekn0e+EV+vhDTbYjx5eLfpMLXsDBwqxBb/4Oxx +64r1EW7tTw2R0hIYLUkVAcKkIhPHEWT/IhKauY5cLwjPcWqzZwFZ8V1G87B4pfYOQnrjfxvM0PC3 +KP0q6p6zsLkEqv32x7SxuCqg+1jxGaBvcCV+PmlKfw8i2O+tCBGaKZnhqkRFmhJePp1tUvznoD1o +L/BLcHwTOK28FSXx1s6rosAx1i+f4P8UWfyEk9mHfExUE+uf0S0R+Bg6Ot4l2ffTQO2kBhLEO+GR +wVY18BTcZTYJbqukB8c10cIDMzZbdSZtQvESa0NvS3GU+jQd7RNuyoB/mC9suWXY6QIDAQABo4HU +MIHRMB0GA1UdDgQWBBQ5lYtii1zJ1IC6WA+XPxUIQ8yYpzALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zCBkQYDVR0jBIGJMIGGgBQ5lYtii1zJ1IC6WA+XPxUIQ8yYp6FrpGkwZzELMAkGA1UE +BhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQLExRBZGRUcnVzdCBUVFAgTmV0d29y +azEjMCEGA1UEAxMaQWRkVHJ1c3QgUXVhbGlmaWVkIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQAD +ggEBABmrder4i2VhlRO6aQTvhsoToMeqT2QbPxj2qC0sVY8FtzDqQmodwCVRLae/DLPt7wh/bDxG +GuoYQ992zPlmhpwsaPXpF/gxsxjE1kh9I0xowX67ARRvxdlu3rsEQmr49lx95dr6h+sNNVJn0J6X +dgWTP5XHAeZpVTh/EGGZyeNfpso+gmNIquIISD6q8rKFYqa0p9m9N5xotS1WfbC3P6CxB9bpT9ze +RXEwMn8bLgn5v1Kh7sKAPgZcLlVAwRv1cEWw3F369nJad9Jjzc9YiQBCYz95OdBEsIJuQRno3eDB +iFrRHnGTHyQwdOUeqN48Jzd/g66ed8/wMLH/S5noxqE= +-----END CERTIFICATE----- + +Entrust Root Certification Authority +==================================== +-----BEGIN CERTIFICATE----- +MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMCVVMxFjAUBgNV +BAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0Lm5ldC9DUFMgaXMgaW5jb3Jw +b3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMWKGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsG +A1UEAxMkRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0 +MloXDTI2MTEyNzIwNTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMu +MTkwNwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSByZWZlcmVu +Y2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNVBAMTJEVudHJ1c3QgUm9v +dCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +ALaVtkNC+sZtKm9I35RMOVcF7sN5EUFoNu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYsz +A9u3g3s+IIRe7bJWKKf44LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOww +Cj0Yzfv9KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGIrb68 +j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi94DkZfs0Nw4pgHBN +rziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOBsDCBrTAOBgNVHQ8BAf8EBAMCAQYw +DwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAigA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1 +MzQyWjAfBgNVHSMEGDAWgBRokORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DH +hmak8fdLQ/uEvW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA +A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9tO1KzKtvn1ISM +Y/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6ZuaAGAT/3B+XxFNSRuzFVJ7yVTa +v52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTS +W3iDVuycNsMm4hH2Z0kdkquM++v/eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0 +tHuu2guQOHXvgR1m0vdXcDazv/wor3ElhVsT/h5/WrQ8 +-----END CERTIFICATE----- + +RSA Security 2048 v3 +==================== +-----BEGIN CERTIFICATE----- +MIIDYTCCAkmgAwIBAgIQCgEBAQAAAnwAAAAKAAAAAjANBgkqhkiG9w0BAQUFADA6MRkwFwYDVQQK +ExBSU0EgU2VjdXJpdHkgSW5jMR0wGwYDVQQLExRSU0EgU2VjdXJpdHkgMjA0OCBWMzAeFw0wMTAy +MjIyMDM5MjNaFw0yNjAyMjIyMDM5MjNaMDoxGTAXBgNVBAoTEFJTQSBTZWN1cml0eSBJbmMxHTAb +BgNVBAsTFFJTQSBTZWN1cml0eSAyMDQ4IFYzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAt49VcdKA3XtpeafwGFAyPGJn9gqVB93mG/Oe2dJBVGutn3y+Gc37RqtBaB4Y6lXIL5F4iSj7 +Jylg/9+PjDvJSZu1pJTOAeo+tWN7fyb9Gd3AIb2E0S1PRsNO3Ng3OTsor8udGuorryGlwSMiuLgb +WhOHV4PR8CDn6E8jQrAApX2J6elhc5SYcSa8LWrg903w8bYqODGBDSnhAMFRD0xS+ARaqn1y07iH +KrtjEAMqs6FPDVpeRrc9DvV07Jmf+T0kgYim3WBU6JU2PcYJk5qjEoAAVZkZR73QpXzDuvsf9/UP ++Ky5tfQ3mBMY3oVbtwyCO4dvlTlYMNpuAWgXIszACwIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/ +MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBQHw1EwpKrpRa41JPr/JCwz0LGdjDAdBgNVHQ4E +FgQUB8NRMKSq6UWuNST6/yQsM9CxnYwwDQYJKoZIhvcNAQEFBQADggEBAF8+hnZuuDU8TjYcHnmY +v/3VEhF5Ug7uMYm83X/50cYVIeiKAVQNOvtUudZj1LGqlk2iQk3UUx+LEN5/Zb5gEydxiKRz44Rj +0aRV4VCT5hsOedBnvEbIvz8XDZXmxpBp3ue0L96VfdASPz0+f00/FGj1EVDVwfSQpQgdMWD/YIwj +VAqv/qFuxdF6Kmh4zx6CCiC0H63lhbJqaHVOrSU3lIW+vaHU6rcMSzyd6BIA8F+sDeGscGNz9395 +nzIlQnQFgCi/vcEkllgVsRch6YlL2weIZ/QVrXA+L02FO8K32/6YaCOJ4XQP3vTFhGMpG8zLB8kA +pKnXwiJPZ9d37CAFYd4= +-----END CERTIFICATE----- + +GeoTrust Global CA +================== +-----BEGIN CERTIFICATE----- +MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVTMRYwFAYDVQQK +Ew1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9iYWwgQ0EwHhcNMDIwNTIxMDQw +MDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5j +LjEbMBkGA1UEAxMSR2VvVHJ1c3QgR2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEA2swYYzD99BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjo +BbdqfnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDviS2Aelet +8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU1XupGc1V3sjs0l44U+Vc +T4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+bw8HHa8sHo9gOeL6NlMTOdReJivbPagU +vTLrGAMoUgRx5aszPeE4uwc2hGKceeoWMPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTAD +AQH/MB0GA1UdDgQWBBTAephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVk +DBF9qn1luMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKInZ57Q +zxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfStQWVYrmm3ok9Nns4 +d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcFPseKUgzbFbS9bZvlxrFUaKnjaZC2 +mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Unhw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6p +XE0zX5IJL4hmXXeXxx12E6nV5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvm +Mw== +-----END CERTIFICATE----- + +GeoTrust Global CA 2 +==================== +-----BEGIN CERTIFICATE----- +MIIDZjCCAk6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBEMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN +R2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3QgR2xvYmFsIENBIDIwHhcNMDQwMzA0MDUw +MDAwWhcNMTkwMzA0MDUwMDAwWjBEMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5j +LjEdMBsGA1UEAxMUR2VvVHJ1c3QgR2xvYmFsIENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQDvPE1APRDfO1MA4Wf+lGAVPoWI8YkNkMgoI5kF6CsgncbzYEbYwbLVjDHZ3CB5JIG/ +NTL8Y2nbsSpr7iFY8gjpeMtvy/wWUsiRxP89c96xPqfCfWbB9X5SJBri1WeR0IIQ13hLTytCOb1k +LUCgsBDTOEhGiKEMuzozKmKY+wCdE1l/bztyqu6mD4b5BWHqZ38MN5aL5mkWRxHCJ1kDs6ZgwiFA +Vvqgx306E+PsV8ez1q6diYD3Aecs9pYrEw15LNnA5IZ7S4wMcoKK+xfNAGw6EzywhIdLFnopsk/b +HdQL82Y3vdj2V7teJHq4PIu5+pIaGoSe2HSPqht/XvT+RSIhAgMBAAGjYzBhMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFHE4NvICMVNHK266ZUapEBVYIAUJMB8GA1UdIwQYMBaAFHE4NvICMVNH +K266ZUapEBVYIAUJMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQUFAAOCAQEAA/e1K6tdEPx7 +srJerJsOflN4WT5CBP51o62sgU7XAotexC3IUnbHLB/8gTKY0UvGkpMzNTEv/NgdRN3ggX+d6Yvh +ZJFiCzkIjKx0nVnZellSlxG5FntvRdOW2TF9AjYPnDtuzywNA0ZF66D0f0hExghAzN4bcLUprbqL +OzRldRtxIR0sFAqwlpW41uryZfspuk/qkZN0abby/+Ea0AzRdoXLiiW9l14sbxWZJue2Kf8i7MkC +x1YAzUm5s2x7UwQa4qjJqhIFI8LO57sEAszAR6LkxCkvW0VXiVHuPOtSCP8HNR6fNWpHSlaY0VqF +H4z1Ir+rzoPz4iIprn2DQKi6bA== +-----END CERTIFICATE----- + +GeoTrust Universal CA +===================== +-----BEGIN CERTIFICATE----- +MIIFaDCCA1CgAwIBAgIBATANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN +R2VvVHJ1c3QgSW5jLjEeMBwGA1UEAxMVR2VvVHJ1c3QgVW5pdmVyc2FsIENBMB4XDTA0MDMwNDA1 +MDAwMFoXDTI5MDMwNDA1MDAwMFowRTELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IElu +Yy4xHjAcBgNVBAMTFUdlb1RydXN0IFVuaXZlcnNhbCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIP +ADCCAgoCggIBAKYVVaCjxuAfjJ0hUNfBvitbtaSeodlyWL0AG0y/YckUHUWCq8YdgNY96xCcOq9t +JPi8cQGeBvV8Xx7BDlXKg5pZMK4ZyzBIle0iN430SppyZj6tlcDgFgDgEB8rMQ7XlFTTQjOgNB0e +RXbdT8oYN+yFFXoZCPzVx5zw8qkuEKmS5j1YPakWaDwvdSEYfyh3peFhF7em6fgemdtzbvQKoiFs +7tqqhZJmr/Z6a4LauiIINQ/PQvE1+mrufislzDoR5G2vc7J2Ha3QsnhnGqQ5HFELZ1aD/ThdDc7d +8Lsrlh/eezJS/R27tQahsiFepdaVaH/wmZ7cRQg+59IJDTWU3YBOU5fXtQlEIGQWFwMCTFMNaN7V +qnJNk22CDtucvc+081xdVHppCZbW2xHBjXWotM85yM48vCR85mLK4b19p71XZQvk/iXttmkQ3Cga +Rr0BHdCXteGYO8A3ZNY9lO4L4fUorgtWv3GLIylBjobFS1J72HGrH4oVpjuDWtdYAVHGTEHZf9hB +Z3KiKN9gg6meyHv8U3NyWfWTehd2Ds735VzZC1U0oqpbtWpU5xPKV+yXbfReBi9Fi1jUIxaS5BZu +KGNZMN9QAZxjiRqf2xeUgnA3wySemkfWWspOqGmJch+RbNt+nhutxx9z3SxPGWX9f5NAEC7S8O08 +ni4oPmkmM8V7AgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNq7LqqwDLiIJlF0 +XG0D08DYj3rWMB8GA1UdIwQYMBaAFNq7LqqwDLiIJlF0XG0D08DYj3rWMA4GA1UdDwEB/wQEAwIB +hjANBgkqhkiG9w0BAQUFAAOCAgEAMXjmx7XfuJRAyXHEqDXsRh3ChfMoWIawC/yOsjmPRFWrZIRc +aanQmjg8+uUfNeVE44B5lGiku8SfPeE0zTBGi1QrlaXv9z+ZhP015s8xxtxqv6fXIwjhmF7DWgh2 +qaavdy+3YL1ERmrvl/9zlcGO6JP7/TG37FcREUWbMPEaiDnBTzynANXH/KttgCJwpQzgXQQpAvvL +oJHRfNbDflDVnVi+QTjruXU8FdmbyUqDWcDaU/0zuzYYm4UPFd3uLax2k7nZAY1IEKj79TiG8dsK +xr2EoyNB3tZ3b4XUhRxQ4K5RirqNPnbiucon8l+f725ZDQbYKxek0nxru18UGkiPGkzns0ccjkxF +KyDuSN/n3QmOGKjaQI2SJhFTYXNd673nxE0pN2HrrDktZy4W1vUAg4WhzH92xH3kt0tm7wNFYGm2 +DFKWkoRepqO1pD4r2czYG0eq8kTaT/kD6PAUyz/zg97QwVTjt+gKN02LIFkDMBmhLMi9ER/frslK +xfMnZmaGrGiR/9nmUxwPi1xpZQomyB40w11Re9epnAahNt3ViZS82eQtDF4JbAiXfKM9fJP/P6EU +p8+1Xevb2xzEdt+Iub1FBZUbrvxGakyvSOPOrg/SfuvmbJxPgWp6ZKy7PtXny3YuxadIwVyQD8vI +P/rmMuGNG2+k5o7Y+SlIis5z/iw= +-----END CERTIFICATE----- + +GeoTrust Universal CA 2 +======================= +-----BEGIN CERTIFICATE----- +MIIFbDCCA1SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBHMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN +R2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVyc2FsIENBIDIwHhcNMDQwMzA0 +MDUwMDAwWhcNMjkwMzA0MDUwMDAwWjBHMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3Qg +SW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVyc2FsIENBIDIwggIiMA0GCSqGSIb3DQEBAQUA +A4ICDwAwggIKAoICAQCzVFLByT7y2dyxUxpZKeexw0Uo5dfR7cXFS6GqdHtXr0om/Nj1XqduGdt0 +DE81WzILAePb63p3NeqqWuDW6KFXlPCQo3RWlEQwAx5cTiuFJnSCegx2oG9NzkEtoBUGFF+3Qs17 +j1hhNNwqCPkuwwGmIkQcTAeC5lvO0Ep8BNMZcyfwqph/Lq9O64ceJHdqXbboW0W63MOhBW9Wjo8Q +JqVJwy7XQYci4E+GymC16qFjwAGXEHm9ADwSbSsVsaxLse4YuU6W3Nx2/zu+z18DwPw76L5GG//a +QMJS9/7jOvdqdzXQ2o3rXhhqMcceujwbKNZrVMaqW9eiLBsZzKIC9ptZvTdrhrVtgrrY6slWvKk2 +WP0+GfPtDCapkzj4T8FdIgbQl+rhrcZV4IErKIM6+vR7IVEAvlI4zs1meaj0gVbi0IMJR1FbUGrP +20gaXT73y/Zl92zxlfgCOzJWgjl6W70viRu/obTo/3+NjN8D8WBOWBFM66M/ECuDmgFz2ZRthAAn +ZqzwcEAJQpKtT5MNYQlRJNiS1QuUYbKHsu3/mjX/hVTK7URDrBs8FmtISgocQIgfksILAAX/8sgC +SqSqqcyZlpwvWOB94b67B9xfBHJcMTTD7F8t4D1kkCLm0ey4Lt1ZrtmhN79UNdxzMk+MBB4zsslG +8dhcyFVQyWi9qLo2CQIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR281Xh+qQ2 ++/CfXGJx7Tz0RzgQKzAfBgNVHSMEGDAWgBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAOBgNVHQ8BAf8E +BAMCAYYwDQYJKoZIhvcNAQEFBQADggIBAGbBxiPz2eAubl/oz66wsCVNK/g7WJtAJDday6sWSf+z +dXkzoS9tcBc0kf5nfo/sm+VegqlVHy/c1FEHEv6sFj4sNcZj/NwQ6w2jqtB8zNHQL1EuxBRa3ugZ +4T7GzKQp5y6EqgYweHZUcyiYWTjgAA1i00J9IZ+uPTqM1fp3DRgrFg5fNuH8KrUwJM/gYwx7WBr+ +mbpCErGR9Hxo4sjoryzqyX6uuyo9DRXcNJW2GHSoag/HtPQTxORb7QrSpJdMKu0vbBKJPfEncKpq +A1Ihn0CoZ1Dy81of398j9tx4TuaYT1U6U+Pv8vSfx3zYWK8pIpe44L2RLrB27FcRz+8pRPPphXpg +Y+RdM4kX2TGq2tbzGDVyz4crL2MjhF2EjD9XoIj8mZEoJmmZ1I+XRL6O1UixpCgp8RW04eWe3fiP +pm8m1wk8OhwRDqZsN/etRIcsKMfYdIKz0G9KV7s1KSegi+ghp4dkNl3M2Basx7InQJJVOCiNUW7d +FGdTbHFcJoRNdVq2fmBWqU2t+5sel/MN2dKXVHfaPRK34B7vCAas+YWH6aLcr34YEoP9VhdBLtUp +gn2Z9DH2canPLAEnpQW5qrJITirvn5NSUZU8UnOOVkwXQMAJKOSLakhT2+zNVVXxxvjpoixMptEm +X36vWkzaH6byHCx+rgIW0lbQL1dTR+iS +-----END CERTIFICATE----- + +America Online Root Certification Authority 1 +============================================= +-----BEGIN CERTIFICATE----- +MIIDpDCCAoygAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEcMBoGA1UEChMT +QW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1lcmljYSBPbmxpbmUgUm9vdCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eSAxMB4XDTAyMDUyODA2MDAwMFoXDTM3MTExOTIwNDMwMFowYzELMAkG +A1UEBhMCVVMxHDAaBgNVBAoTE0FtZXJpY2EgT25saW5lIEluYy4xNjA0BgNVBAMTLUFtZXJpY2Eg +T25saW5lIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMTCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAKgv6KRpBgNHw+kqmP8ZonCaxlCyfqXfaE0bfA+2l2h9LaaLl+lkhsmj76CG +v2BlnEtUiMJIxUo5vxTjWVXlGbR0yLQFOVwWpeKVBeASrlmLojNoWBym1BW32J/X3HGrfpq/m44z +DyL9Hy7nBzbvYjnF3cu6JRQj3gzGPTzOggjmZj7aUTsWOqMFf6Dch9Wc/HKpoH145LcxVR5lu9Rh +sCFg7RAycsWSJR74kEoYeEfffjA3PlAb2xzTa5qGUwew76wGePiEmf4hjUyAtgyC9mZweRrTT6PP +8c9GsEsPPt2IYriMqQkoO3rHl+Ee5fSfwMCuJKDIodkP1nsmgmkyPacCAwEAAaNjMGEwDwYDVR0T +AQH/BAUwAwEB/zAdBgNVHQ4EFgQUAK3Zo/Z59m50qX8zPYEX10zPM94wHwYDVR0jBBgwFoAUAK3Z +o/Z59m50qX8zPYEX10zPM94wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBBQUAA4IBAQB8itEf +GDeC4Liwo+1WlchiYZwFos3CYiZhzRAW18y0ZTTQEYqtqKkFZu90821fnZmv9ov761KyBZiibyrF +VL0lvV+uyIbqRizBs73B6UlwGBaXCBOMIOAbLjpHyx7kADCVW/RFo8AasAFOq73AI25jP4BKxQft +3OJvx8Fi8eNy1gTIdGcL+oiroQHIb/AUr9KZzVGTfu0uOMe9zkZQPXLjeSWdm4grECDdpbgyn43g +Kd8hdIaC2y+CMMbHNYaz+ZZfRtsMRf3zUMNvxsNIrUam4SdHCh0Om7bCd39j8uB9Gr784N/Xx6ds +sPmuujz9dLQR6FgNgLzTqIA6me11zEZ7 +-----END CERTIFICATE----- + +America Online Root Certification Authority 2 +============================================= +-----BEGIN CERTIFICATE----- +MIIFpDCCA4ygAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEcMBoGA1UEChMT +QW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1lcmljYSBPbmxpbmUgUm9vdCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eSAyMB4XDTAyMDUyODA2MDAwMFoXDTM3MDkyOTE0MDgwMFowYzELMAkG +A1UEBhMCVVMxHDAaBgNVBAoTE0FtZXJpY2EgT25saW5lIEluYy4xNjA0BgNVBAMTLUFtZXJpY2Eg +T25saW5lIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMjCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBAMxBRR3pPU0Q9oyxQcngXssNt79Hc9PwVU3dxgz6sWYFas14tNwC206B89en +fHG8dWOgXeMHDEjsJcQDIPT/DjsS/5uN4cbVG7RtIuOx238hZK+GvFciKtZHgVdEglZTvYYUAQv8 +f3SkWq7xuhG1m1hagLQ3eAkzfDJHA1zEpYNI9FdWboE2JxhP7JsowtS013wMPgwr38oE18aO6lhO +qKSlGBxsRZijQdEt0sdtjRnxrXm3gT+9BoInLRBYBbV4Bbkv2wxrkJB+FFk4u5QkE+XRnRTf04JN +RvCAOVIyD+OEsnpD8l7eXz8d3eOyG6ChKiMDbi4BFYdcpnV1x5dhvt6G3NRI270qv0pV2uh9UPu0 +gBe4lL8BPeraunzgWGcXuVjgiIZGZ2ydEEdYMtA1fHkqkKJaEBEjNa0vzORKW6fIJ/KD3l67Xnfn +6KVuY8INXWHQjNJsWiEOyiijzirplcdIz5ZvHZIlyMbGwcEMBawmxNJ10uEqZ8A9W6Wa6897Gqid +FEXlD6CaZd4vKL3Ob5Rmg0gp2OpljK+T2WSfVVcmv2/LNzGZo2C7HK2JNDJiuEMhBnIMoVxtRsX6 +Kc8w3onccVvdtjc+31D1uAclJuW8tf48ArO3+L5DwYcRlJ4jbBeKuIonDFRH8KmzwICMoCfrHRnj +B453cMor9H124HhnAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFE1FwWg4u3Op +aaEg5+31IqEjFNeeMB8GA1UdIwQYMBaAFE1FwWg4u3OpaaEg5+31IqEjFNeeMA4GA1UdDwEB/wQE +AwIBhjANBgkqhkiG9w0BAQUFAAOCAgEAZ2sGuV9FOypLM7PmG2tZTiLMubekJcmnxPBUlgtk87FY +T15R/LKXeydlwuXK5w0MJXti4/qftIe3RUavg6WXSIylvfEWK5t2LHo1YGwRgJfMqZJS5ivmae2p ++DYtLHe/YUjRYwu5W1LtGLBDQiKmsXeu3mnFzcccobGlHBD7GL4acN3Bkku+KVqdPzW+5X1R+FXg +JXUjhx5c3LqdsKyzadsXg8n33gy8CNyRnqjQ1xU3c6U1uPx+xURABsPr+CKAXEfOAuMRn0T//Zoy +zH1kUQ7rVyZ2OuMeIjzCpjbdGe+n/BLzJsBZMYVMnNjP36TMzCmT/5RtdlwTCJfy7aULTd3oyWgO +ZtMADjMSW7yV5TKQqLPGbIOtd+6Lfn6xqavT4fG2wLHqiMDn05DpKJKUe2h7lyoKZy2FAjgQ5ANh +1NolNscIWC2hp1GvMApJ9aZphwctREZ2jirlmjvXGKL8nDgQzMY70rUXOm/9riW99XJZZLF0Kjhf +GEzfz3EEWjbUvy+ZnOjZurGV5gJLIaFb1cFPj65pbVPbAZO1XB4Y3WRayhgoPmMEEf0cjQAPuDff +Z4qdZqkCapH/E8ovXYO8h5Ns3CRRFgQlZvqz2cK6Kb6aSDiCmfS/O0oxGfm/jiEzFMpPVF/7zvuP +cX/9XhmgD0uRuMRUvAawRY8mkaKO/qk= +-----END CERTIFICATE----- + +Visa eCommerce Root +=================== +-----BEGIN CERTIFICATE----- +MIIDojCCAoqgAwIBAgIQE4Y1TR0/BvLB+WUF1ZAcYjANBgkqhkiG9w0BAQUFADBrMQswCQYDVQQG +EwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlzYSBJbnRlcm5hdGlvbmFsIFNlcnZpY2Ug +QXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNvbW1lcmNlIFJvb3QwHhcNMDIwNjI2MDIxODM2 +WhcNMjIwNjI0MDAxNjEyWjBrMQswCQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMm +VmlzYSBJbnRlcm5hdGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNv +bW1lcmNlIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvV95WHm6h2mCxlCfL +F9sHP4CFT8icttD0b0/Pmdjh28JIXDqsOTPHH2qLJj0rNfVIsZHBAk4ElpF7sDPwsRROEW+1QK8b +RaVK7362rPKgH1g/EkZgPI2h4H3PVz4zHvtH8aoVlwdVZqW1LS7YgFmypw23RuwhY/81q6UCzyr0 +TP579ZRdhE2o8mCP2w4lPJ9zcc+U30rq299yOIzzlr3xF7zSujtFWsan9sYXiwGd/BmoKoMWuDpI +/k4+oKsGGelT84ATB+0tvz8KPFUgOSwsAGl0lUq8ILKpeeUYiZGo3BxN77t+Nwtd/jmliFKMAGzs +GHxBvfaLdXe6YJ2E5/4tAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEG +MB0GA1UdDgQWBBQVOIMPPyw/cDMezUb+B4wg4NfDtzANBgkqhkiG9w0BAQUFAAOCAQEAX/FBfXxc +CLkr4NWSR/pnXKUTwwMhmytMiUbPWU3J/qVAtmPN3XEolWcRzCSs00Rsca4BIGsDoo8Ytyk6feUW +YFN4PMCvFYP3j1IzJL1kk5fui/fbGKhtcbP3LBfQdCVp9/5rPJS+TUtBjE7ic9DjkCJzQ83z7+pz +zkWKsKZJ/0x9nXGIxHYdkFsd7v3M9+79YKWxehZx0RbQfBI8bGmX265fOZpwLwU8GUYEmSA20GBu +YQa7FkKMcPcw++DbZqMAAb3mLNqRX6BGi01qnD093QVG/na/oAo85ADmJ7f/hC3euiInlhBx6yLt +398znM/jra6O1I7mT1GvFpLgXPYHDw== +-----END CERTIFICATE----- + +Certum Root CA +============== +-----BEGIN CERTIFICATE----- +MIIDDDCCAfSgAwIBAgIDAQAgMA0GCSqGSIb3DQEBBQUAMD4xCzAJBgNVBAYTAlBMMRswGQYDVQQK +ExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBDQTAeFw0wMjA2MTExMDQ2Mzla +Fw0yNzA2MTExMDQ2MzlaMD4xCzAJBgNVBAYTAlBMMRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8u +by4xEjAQBgNVBAMTCUNlcnR1bSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM6x +wS7TT3zNJc4YPk/EjG+AanPIW1H4m9LcuwBcsaD8dQPugfCI7iNS6eYVM42sLQnFdvkrOYCJ5JdL +kKWoePhzQ3ukYbDYWMzhbGZ+nPMJXlVjhNWo7/OxLjBos8Q82KxujZlakE403Daaj4GIULdtlkIJ +89eVgw1BS7Bqa/j8D35in2fE7SZfECYPCE/wpFcozo+47UX2bu4lXapuOb7kky/ZR6By6/qmW6/K +Uz/iDsaWVhFu9+lmqSbYf5VT7QqFiLpPKaVCjF62/IUgAKpoC6EahQGcxEZjgoi2IrHu/qpGWX7P +NSzVttpd90gzFFS269lvzs2I1qsb2pY7HVkCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkq +hkiG9w0BAQUFAAOCAQEAuI3O7+cUus/usESSbLQ5PqKEbq24IXfS1HeCh+YgQYHu4vgRt2PRFze+ +GXYkHAQaTOs9qmdvLdTN/mUxcMUbpgIKumB7bVjCmkn+YzILa+M6wKyrO7Do0wlRjBCDxjTgxSvg +GrZgFCdsMneMvLJymM/NzD+5yCRCFNZX/OYmQ6kd5YCQzgNUKD73P9P4Te1qCjqTE5s7FCMTY5w/ +0YcneeVMUeMBrYVdGjux1XMQpNPyvG5k9VpWkKjHDkx0Dy5xO/fIR/RpbxXyEV6DHpx8Uq79AtoS +qFlnGNu8cN2bsWntgM6JQEhqDjXKKWYVIZQs6GAqm4VKQPNriiTsBhYscw== +-----END CERTIFICATE----- + +Comodo AAA Services root +======================== +-----BEGIN CERTIFICATE----- +MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS +R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg +TGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAw +MFoXDTI4MTIzMTIzNTk1OVowezELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hl +c3RlcjEQMA4GA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNV +BAMMGEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQuaBtDFcCLNSS1UY8y2bmhG +C1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe3M/vg4aijJRPn2jymJBGhCfHdr/jzDUs +i14HZGWCwEiwqJH5YZ92IFCokcdmtet4YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszW +Y19zjNoFmag4qMsXeDZRrOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjH +Ypy+g8cmez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQUoBEK +Iz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wewYDVR0f +BHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20vQUFBQ2VydGlmaWNhdGVTZXJ2aWNl +cy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29tb2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2Vz +LmNybDANBgkqhkiG9w0BAQUFAAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm +7l3sAg9g1o1QGE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz +Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2G9w84FoVxp7Z +8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsil2D4kF501KKaU73yqWjgom7C +12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg== +-----END CERTIFICATE----- + +Comodo Secure Services root +=========================== +-----BEGIN CERTIFICATE----- +MIIEPzCCAyegAwIBAgIBATANBgkqhkiG9w0BAQUFADB+MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS +R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg +TGltaXRlZDEkMCIGA1UEAwwbU2VjdXJlIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAw +MDAwMFoXDTI4MTIzMTIzNTk1OVowfjELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFu +Y2hlc3RlcjEQMA4GA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxJDAi +BgNVBAMMG1NlY3VyZSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAMBxM4KK0HDrc4eCQNUd5MvJDkKQ+d40uaG6EfQlhfPMcm3ye5drswfxdySRXyWP +9nQ95IDC+DwN879A6vfIUtFyb+/Iq0G4bi4XKpVpDM3SHpR7LZQdqnXXs5jLrLxkU0C8j6ysNstc +rbvd4JQX7NFc0L/vpZXJkMWwrPsbQ996CF23uPJAGysnnlDOXmWCiIxe004MeuoIkbY2qitC++rC +oznl2yY4rYsK7hljxxwk3wN42ubqwUcaCwtGCd0C/N7Lh1/XMGNooa7cMqG6vv5Eq2i2pRcV/b3V +p6ea5EQz6YiO/O1R65NxTq0B50SOqy3LqP4BSUjwwN3HaNiS/j0CAwEAAaOBxzCBxDAdBgNVHQ4E +FgQUPNiTiMLAggnMAZkGkyDpnnAJY08wDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8w +gYEGA1UdHwR6MHgwO6A5oDeGNWh0dHA6Ly9jcmwuY29tb2RvY2EuY29tL1NlY3VyZUNlcnRpZmlj +YXRlU2VydmljZXMuY3JsMDmgN6A1hjNodHRwOi8vY3JsLmNvbW9kby5uZXQvU2VjdXJlQ2VydGlm +aWNhdGVTZXJ2aWNlcy5jcmwwDQYJKoZIhvcNAQEFBQADggEBAIcBbSMdflsXfcFhMs+P5/OKlFlm +4J4oqF7Tt/Q05qo5spcWxYJvMqTpjOev/e/C6LlLqqP05tqNZSH7uoDrJiiFGv45jN5bBAS0VPmj +Z55B+glSzAVIqMk/IQQezkhr/IXownuvf7fM+F86/TXGDe+X3EyrEeFryzHRbPtIgKvcnDe4IRRL +DXE97IMzbtFuMhbsmMcWi1mmNKsFVy2T96oTy9IT4rcuO81rUBcJaD61JlfutuC23bkpgHl9j6Pw +pCikFcSF9CfUa7/lXORlAnZUtOM3ZiTTGWHIUhDlizeauan5Hb/qmZJhlv8BzaFfDbxxvA6sCx1H +RR3B7Hzs/Sk= +-----END CERTIFICATE----- + +Comodo Trusted Services root +============================ +-----BEGIN CERTIFICATE----- +MIIEQzCCAyugAwIBAgIBATANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS +R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg +TGltaXRlZDElMCMGA1UEAwwcVHJ1c3RlZCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczAeFw0wNDAxMDEw +MDAwMDBaFw0yODEyMzEyMzU5NTlaMH8xCzAJBgNVBAYTAkdCMRswGQYDVQQIDBJHcmVhdGVyIE1h +bmNoZXN0ZXIxEDAOBgNVBAcMB1NhbGZvcmQxGjAYBgNVBAoMEUNvbW9kbyBDQSBMaW1pdGVkMSUw +IwYDVQQDDBxUcnVzdGVkIENlcnRpZmljYXRlIFNlcnZpY2VzMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEA33FvNlhTWvI2VFeAxHQIIO0Yfyod5jWaHiWsnOWWfnJSoBVC21ndZHoa0Lh7 +3TkVvFVIxO06AOoxEbrycXQaZ7jPM8yoMa+j49d/vzMtTGo87IvDktJTdyR0nAducPy9C1t2ul/y +/9c3S0pgePfw+spwtOpZqqPOSC+pw7ILfhdyFgymBwwbOM/JYrc/oJOlh0Hyt3BAd9i+FHzjqMB6 +juljatEPmsbS9Is6FARW1O24zG71++IsWL1/T2sr92AkWCTOJu80kTrV44HQsvAEAtdbtz6SrGsS +ivnkBbA7kUlcsutT6vifR4buv5XAwAaf0lteERv0xwQ1KdJVXOTt6wIDAQABo4HJMIHGMB0GA1Ud +DgQWBBTFe1i97doladL3WRaoszLAeydb9DAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zCBgwYDVR0fBHwwejA8oDqgOIY2aHR0cDovL2NybC5jb21vZG9jYS5jb20vVHJ1c3RlZENlcnRp +ZmljYXRlU2VydmljZXMuY3JsMDqgOKA2hjRodHRwOi8vY3JsLmNvbW9kby5uZXQvVHJ1c3RlZENl +cnRpZmljYXRlU2VydmljZXMuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQDIk4E7ibSvuIQSTI3S8Ntw +uleGFTQQuS9/HrCoiWChisJ3DFBKmwCL2Iv0QeLQg4pKHBQGsKNoBXAxMKdTmw7pSqBYaWcOrp32 +pSxBvzwGa+RZzG0Q8ZZvH9/0BAKkn0U+yNj6NkZEUD+Cl5EfKNsYEYwq5GWDVxISjBc/lDb+XbDA +BHcTuPQV1T84zJQ6VdCsmPW6AF/ghhmBeC8owH7TzEIK9a5QoNE+xqFx7D+gIIxmOom0jtTYsU0l +R+4viMi14QVFwL4Ucd56/Y57fU0IlqUSc/AtyjcndBInTMu2l+nZrghtWjlA3QVHdWpaIbOjGM9O +9y5Xt5hwXsjEeLBi +-----END CERTIFICATE----- + +QuoVadis Root CA +================ +-----BEGIN CERTIFICATE----- +MIIF0DCCBLigAwIBAgIEOrZQizANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJCTTEZMBcGA1UE +ChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0 +eTEuMCwGA1UEAxMlUXVvVmFkaXMgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMTAz +MTkxODMzMzNaFw0yMTAzMTcxODMzMzNaMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRp +cyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQD +EyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAv2G1lVO6V/z68mcLOhrfEYBklbTRvM16z/Ypli4kVEAkOPcahdxYTMuk +J0KX0J+DisPkBgNbAKVRHnAEdOLB1Dqr1607BxgFjv2DrOpm2RgbaIr1VxqYuvXtdj182d6UajtL +F8HVj71lODqV0D1VNk7feVcxKh7YWWVJWCCYfqtffp/p1k3sg3Spx2zY7ilKhSoGFPlU5tPaZQeL +YzcS19Dsw3sgQUSj7cugF+FxZc4dZjH3dgEZyH0DWLaVSR2mEiboxgx24ONmy+pdpibu5cxfvWen +AScOospUxbF6lR1xHkopigPcakXBpBlebzbNw6Kwt/5cOOJSvPhEQ+aQuwIDAQABo4ICUjCCAk4w +PQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwczovL29jc3AucXVvdmFkaXNvZmZzaG9y +ZS5jb20wDwYDVR0TAQH/BAUwAwEB/zCCARoGA1UdIASCAREwggENMIIBCQYJKwYBBAG+WAABMIH7 +MIHUBggrBgEFBQcCAjCBxxqBxFJlbGlhbmNlIG9uIHRoZSBRdW9WYWRpcyBSb290IENlcnRpZmlj +YXRlIGJ5IGFueSBwYXJ0eSBhc3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJs +ZSBzdGFuZGFyZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRpb24gcHJh +Y3RpY2VzLCBhbmQgdGhlIFF1b1ZhZGlzIENlcnRpZmljYXRlIFBvbGljeS4wIgYIKwYBBQUHAgEW +Fmh0dHA6Ly93d3cucXVvdmFkaXMuYm0wHQYDVR0OBBYEFItLbe3TKbkGGew5Oanwl4Rqy+/fMIGu +BgNVHSMEgaYwgaOAFItLbe3TKbkGGew5Oanwl4Rqy+/foYGEpIGBMH8xCzAJBgNVBAYTAkJNMRkw +FwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MS4wLAYDVQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggQ6 +tlCLMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAitQUtf70mpKnGdSkfnIYj9lo +fFIk3WdvOXrEql494liwTXCYhGHoG+NpGA7O+0dQoE7/8CQfvbLO9Sf87C9TqnN7Az10buYWnuul +LsS/VidQK2K6vkscPFVcQR0kvoIgR13VRH56FmjffU1RcHhXHTMe/QKZnAzNCgVPx7uOpHX6Sm2x +gI4JVrmcGmD+XcHXetwReNDWXcG31a0ymQM6isxUJTkxgXsTIlG6Rmyhu576BGxJJnSP0nPrzDCi +5upZIof4l/UO/erMkqQWxFIY6iHOsfHmhIHluqmGKPJDWl0Snawe2ajlCmqnf6CHKc/yiU3U7MXi +5nrQNiOKSnQ2+Q== +-----END CERTIFICATE----- + +QuoVadis Root CA 2 +================== +-----BEGIN CERTIFICATE----- +MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoT +EFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJvb3QgQ0EgMjAeFw0wNjExMjQx +ODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4IC +DwAwggIKAoICAQCaGMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4GtMh6QRr+jhiYaHv5+HBg6 +XJxgFyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp3MJGF/hd/aTa/55JWpzmM+Yk +lvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02kfN/+NsRE8Scd3bBrrcCaoF6qUWD4gXmuVbB +lDePSHFjIuwXZQeVikvfj8ZaCuWw419eaxGrDPmF60Tp+ARz8un+XJiM9XOva7R+zdRcAitMOeGy +lZUtQofX1bOQQ7dsE/He3fbE+Ik/0XX1ksOR1YqI0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt +66/3FsvbzSUr5R/7mp/iUcw6UwxI5g69ybR2BlLmEROFcmMDBOAENisgGQLodKcftslWZvB1Jdxn +wQ5hYIizPtGo/KPaHbDRsSNU30R2be1B2MGyIrZTHN81Hdyhdyox5C315eXbyOD/5YDXC2Og/zOh +D7osFRXql7PSorW+8oyWHhqPHWykYTe5hnMz15eWniN9gqRMgeKh0bpnX5UHoycR7hYQe7xFSkyy +BNKr79X9DFHOUGoIMfmR2gyPZFwDwzqLID9ujWc9Otb+fVuIyV77zGHcizN300QyNQliBJIWENie +J0f7OyHj+OsdWwIDAQABo4GwMIGtMA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1Ud +DgQWBBQahGK8SEwzJQTU7tD2A8QZRtGUazBuBgNVHSMEZzBlgBQahGK8SEwzJQTU7tD2A8QZRtGU +a6FJpEcwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMT +ElF1b1ZhZGlzIFJvb3QgQ0EgMoICBQkwDQYJKoZIhvcNAQEFBQADggIBAD4KFk2fBluornFdLwUv +Z+YTRYPENvbzwCYMDbVHZF34tHLJRqUDGCdViXh9duqWNIAXINzng/iN/Ae42l9NLmeyhP3ZRPx3 +UIHmfLTJDQtyU/h2BwdBR5YM++CCJpNVjP4iH2BlfF/nJrP3MpCYUNQ3cVX2kiF495V5+vgtJodm +VjB3pjd4M1IQWK4/YY7yarHvGH5KWWPKjaJW1acvvFYfzznB4vsKqBUsfU16Y8Zsl0Q80m/DShcK ++JDSV6IZUaUtl0HaB0+pUNqQjZRG4T7wlP0QADj1O+hA4bRuVhogzG9Yje0uRY/W6ZM/57Es3zrW +IozchLsib9D45MY56QSIPMO661V6bYCZJPVsAfv4l7CUW+v90m/xd2gNNWQjrLhVoQPRTUIZ3Ph1 +WVaj+ahJefivDrkRoHy3au000LYmYjgahwz46P0u05B/B5EqHdZ+XIWDmbA4CD/pXvk1B+TJYm5X +f6dQlfe6yJvmjqIBxdZmv3lh8zwc4bmCXF2gw+nYSL0ZohEUGW6yhhtoPkg3Goi3XZZenMfvJ2II +4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y4aOTHcyKJloJONDO1w2AFrR4pTqHTI2KpdVGl/IsELm8 +VCLAAVBpQ570su9t+Oza8eOx79+Rj1QqCyXBJhnEUhAFZdWCEOrCMc0u +-----END CERTIFICATE----- + +QuoVadis Root CA 3 +================== +-----BEGIN CERTIFICATE----- +MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoT +EFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJvb3QgQ0EgMzAeFw0wNjExMjQx +OTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4IC +DwAwggIKAoICAQDMV0IWVJzmmNPTTe7+7cefQzlKZbPoFog02w1ZkXTPkrgEQK0CSzGrvI2RaNgg +DhoB4hp7Thdd4oq3P5kazethq8Jlph+3t723j/z9cI8LoGe+AaJZz3HmDyl2/7FWeUUrH556VOij +KTVopAFPD6QuN+8bv+OPEKhyq1hX51SGyMnzW9os2l2ObjyjPtr7guXd8lyyBTNvijbO0BNO/79K +DDRMpsMhvVAEVeuxu537RR5kFd5VAYwCdrXLoT9CabwvvWhDFlaJKjdhkf2mrk7AyxRllDdLkgbv +BNDInIjbC3uBr7E9KsRlOni27tyAsdLTmZw67mtaa7ONt9XOnMK+pUsvFrGeaDsGb659n/je7Mwp +p5ijJUMv7/FfJuGITfhebtfZFG4ZM2mnO4SJk8RTVROhUXhA+LjJou57ulJCg54U7QVSWllWp5f8 +nT8KKdjcT5EOE7zelaTfi5m+rJsziO+1ga8bxiJTyPbH7pcUsMV8eFLI8M5ud2CEpukqdiDtWAEX +MJPpGovgc2PZapKUSU60rUqFxKMiMPwJ7Wgic6aIDFUhWMXhOp8q3crhkODZc6tsgLjoC2SToJyM +Gf+z0gzskSaHirOi4XCPLArlzW1oUevaPwV/izLmE1xr/l9A4iLItLRkT9a6fUg+qGkM17uGcclz +uD87nSVL2v9A6wIDAQABo4IBlTCCAZEwDwYDVR0TAQH/BAUwAwEB/zCB4QYDVR0gBIHZMIHWMIHT +BgkrBgEEAb5YAAMwgcUwgZMGCCsGAQUFBwICMIGGGoGDQW55IHVzZSBvZiB0aGlzIENlcnRpZmlj +YXRlIGNvbnN0aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFF1b1ZhZGlzIFJvb3QgQ0EgMyBDZXJ0 +aWZpY2F0ZSBQb2xpY3kgLyBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVudC4wLQYIKwYB +BQUHAgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2NwczALBgNVHQ8EBAMCAQYwHQYD +VR0OBBYEFPLAE+CCQz777i9nMpY1XNu4ywLQMG4GA1UdIwRnMGWAFPLAE+CCQz777i9nMpY1XNu4 +ywLQoUmkRzBFMQswCQYDVQQGEwJCTTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDEbMBkGA1UE +AxMSUXVvVmFkaXMgUm9vdCBDQSAzggIFxjANBgkqhkiG9w0BAQUFAAOCAgEAT62gLEz6wPJv92ZV +qyM07ucp2sNbtrCD2dDQ4iH782CnO11gUyeim/YIIirnv6By5ZwkajGxkHon24QRiSemd1o417+s +hvzuXYO8BsbRd2sPbSQvS3pspweWyuOEn62Iix2rFo1bZhfZFvSLgNLd+LJ2w/w4E6oM3kJpK27z +POuAJ9v1pkQNn1pVWQvVDVJIxa6f8i+AxeoyUDUSly7B4f/xI4hROJ/yZlZ25w9Rl6VSDE1JUZU2 +Pb+iSwwQHYaZTKrzchGT5Or2m9qoXadNt54CrnMAyNojA+j56hl0YgCUyyIgvpSnWbWCar6ZeXqp +8kokUvd0/bpO5qgdAm6xDYBEwa7TIzdfu4V8K5Iu6H6li92Z4b8nby1dqnuH/grdS/yO9SbkbnBC +bjPsMZ57k8HkyWkaPcBrTiJt7qtYTcbQQcEr6k8Sh17rRdhs9ZgC06DYVYoGmRmioHfRMJ6szHXu +g/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7j2G4aSgWQgRecCocIdiP4b0jWy10QJLZYxkNc91p +vGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeTmJlglFwjz1onl14LBQaTNx47aTbr +qZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK4SVhM7JZG+Ju1zdXtg2pEto= +-----END CERTIFICATE----- + +Security Communication Root CA +============================== +-----BEGIN CERTIFICATE----- +MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMP +U0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEw +HhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMP +U0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8V6UMbXaKL0u/ZPtM7orw +8yl89f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzGjGdnSj74cbAZJ6kJDKaVv0uM +DPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9Ms+k2Y7CI9eNqPPYJayX +5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/NQV3Is00qVUarH9oe4kA92819uZKAnDfd +DJZkndwi92SL32HeFZRSFaB9UslLqCHJxrHty8OVYNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2 +JChzAgMBAAGjPzA9MB0GA1UdDgQWBBSgc0mZaNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYw +DwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vGkl3g +0dNq/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfrUj94nK9NrvjVT8+a +mCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5Bw+SUEmK3TGXX8npN6o7WWWXlDLJ +s58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJUJRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ +6rBK+1YWc26sTfcioU+tHXotRSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAi +FL39vmwLAw== +-----END CERTIFICATE----- + +Sonera Class 2 Root CA +====================== +-----BEGIN CERTIFICATE----- +MIIDIDCCAgigAwIBAgIBHTANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEPMA0GA1UEChMG +U29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MyIENBMB4XDTAxMDQwNjA3Mjk0MFoXDTIxMDQw +NjA3Mjk0MFowOTELMAkGA1UEBhMCRkkxDzANBgNVBAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJh +IENsYXNzMiBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJAXSjWdyvANlsdE+hY3 +/Ei9vX+ALTU74W+oZ6m/AxxNjG8yR9VBaKQTBME1DJqEQ/xcHf+Js+gXGM2RX/uJ4+q/Tl18GybT +dXnt5oTjV+WtKcT0OijnpXuENmmz/V52vaMtmdOQTiMofRhj8VQ7Jp12W5dCsv+u8E7s3TmVToMG +f+dJQMjFAbJUWmYdPfz56TwKnoG4cPABi+QjVHzIrviQHgCWctRUz2EjvOr7nQKV0ba5cTppCD8P +tOFCx4j1P5iop7oc4HFx71hXgVB6XGt0Rg6DA5jDjqhu8nYybieDwnPz3BjotJPqdURrBGAgcVeH +nfO+oJAjPYok4doh28MCAwEAAaMzMDEwDwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQISqCqWITT +XjwwCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQBazof5FnIVV0sd2ZvnoiYw7JNn39Yt +0jSv9zilzqsWuasvfDXLrNAPtEwr/IDva4yRXzZ299uzGxnq9LIR/WFxRL8oszodv7ND6J+/3DEI +cbCdjdY0RzKQxmUk96BKfARzjzlvF4xytb1LyHr4e4PDKE6cCepnP7JnBBvDFNr450kkkdAdavph +Oe9r5yF1BgfYErQhIHBCcYHaPJo2vqZbDWpsmh+Re/n570K6Tk6ezAyNlNzZRZxe7EJQY670XcSx +EtzKO6gunRRaBXW37Ndj4ro1tgQIkejanZz2ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLH +llpwrN9M +-----END CERTIFICATE----- + +Staat der Nederlanden Root CA +============================= +-----BEGIN CERTIFICATE----- +MIIDujCCAqKgAwIBAgIEAJiWijANBgkqhkiG9w0BAQUFADBVMQswCQYDVQQGEwJOTDEeMBwGA1UE +ChMVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSYwJAYDVQQDEx1TdGFhdCBkZXIgTmVkZXJsYW5kZW4g +Um9vdCBDQTAeFw0wMjEyMTcwOTIzNDlaFw0xNTEyMTYwOTE1MzhaMFUxCzAJBgNVBAYTAk5MMR4w +HAYDVQQKExVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xJjAkBgNVBAMTHVN0YWF0IGRlciBOZWRlcmxh +bmRlbiBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmNK1URF6gaYUmHFt +vsznExvWJw56s2oYHLZhWtVhCb/ekBPHZ+7d89rFDBKeNVU+LCeIQGv33N0iYfXCxw719tV2U02P +jLwYdjeFnejKScfST5gTCaI+Ioicf9byEGW07l8Y1Rfj+MX94p2i71MOhXeiD+EwR+4A5zN9RGca +C1Hoi6CeUJhoNFIfLm0B8mBF8jHrqTFoKbt6QZ7GGX+UtFE5A3+y3qcym7RHjm+0Sq7lr7HcsBth +vJly3uSJt3omXdozSVtSnA71iq3DuD3oBmrC1SoLbHuEvVYFy4ZlkuxEK7COudxwC0barbxjiDn6 +22r+I/q85Ej0ZytqERAhSQIDAQABo4GRMIGOMAwGA1UdEwQFMAMBAf8wTwYDVR0gBEgwRjBEBgRV +HSAAMDwwOgYIKwYBBQUHAgEWLmh0dHA6Ly93d3cucGtpb3ZlcmhlaWQubmwvcG9saWNpZXMvcm9v +dC1wb2xpY3kwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSofeu8Y6R0E3QA7Jbg0zTBLL9s+DAN +BgkqhkiG9w0BAQUFAAOCAQEABYSHVXQ2YcG70dTGFagTtJ+k/rvuFbQvBgwp8qiSpGEN/KtcCFtR +EytNwiphyPgJWPwtArI5fZlmgb9uXJVFIGzmeafR2Bwp/MIgJ1HI8XxdNGdphREwxgDS1/PTfLbw +MVcoEoJz6TMvplW0C5GUR5z6u3pCMuiufi3IvKwUv9kP2Vv8wfl6leF9fpb8cbDCTMjfRTTJzg3y +nGQI0DvDKcWy7ZAEwbEpkcUwb8GpcjPM/l0WFywRaed+/sWDCN+83CI6LiBpIzlWYGeQiy52OfsR +iJf2fL1LuCAWZwWN4jvBcj+UlTfHXbme2JOhF4//DGYVwSR8MnwDHTuhWEUykw== +-----END CERTIFICATE----- + +TDC Internet Root CA +==================== +-----BEGIN CERTIFICATE----- +MIIEKzCCAxOgAwIBAgIEOsylTDANBgkqhkiG9w0BAQUFADBDMQswCQYDVQQGEwJESzEVMBMGA1UE +ChMMVERDIEludGVybmV0MR0wGwYDVQQLExRUREMgSW50ZXJuZXQgUm9vdCBDQTAeFw0wMTA0MDUx +NjMzMTdaFw0yMTA0MDUxNzAzMTdaMEMxCzAJBgNVBAYTAkRLMRUwEwYDVQQKEwxUREMgSW50ZXJu +ZXQxHTAbBgNVBAsTFFREQyBJbnRlcm5ldCBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAxLhAvJHVYx/XmaCLDEAedLdInUaMArLgJF/wGROnN4NrXceO+YQwzho7+vvOi20j +xsNuZp+Jpd/gQlBn+h9sHvTQBda/ytZO5GhgbEaqHF1j4QeGDmUApy6mcca8uYGoOn0a0vnRrEvL +znWv3Hv6gXPU/Lq9QYjUdLP5Xjg6PEOo0pVOd20TDJ2PeAG3WiAfAzc14izbSysseLlJ28TQx5yc +5IogCSEWVmb/Bexb4/DPqyQkXsN/cHoSxNK1EKC2IeGNeGlVRGn1ypYcNIUXJXfi9i8nmHj9eQY6 +otZaQ8H/7AQ77hPv01ha/5Lr7K7a8jcDR0G2l8ktCkEiu7vmpwIDAQABo4IBJTCCASEwEQYJYIZI +AYb4QgEBBAQDAgAHMGUGA1UdHwReMFwwWqBYoFakVDBSMQswCQYDVQQGEwJESzEVMBMGA1UEChMM +VERDIEludGVybmV0MR0wGwYDVQQLExRUREMgSW50ZXJuZXQgUm9vdCBDQTENMAsGA1UEAxMEQ1JM +MTArBgNVHRAEJDAigA8yMDAxMDQwNTE2MzMxN1qBDzIwMjEwNDA1MTcwMzE3WjALBgNVHQ8EBAMC +AQYwHwYDVR0jBBgwFoAUbGQBx/2FbazI2p5QCIUItTxWqFAwHQYDVR0OBBYEFGxkAcf9hW2syNqe +UAiFCLU8VqhQMAwGA1UdEwQFMAMBAf8wHQYJKoZIhvZ9B0EABBAwDhsIVjUuMDo0LjADAgSQMA0G +CSqGSIb3DQEBBQUAA4IBAQBOQ8zR3R0QGwZ/t6T609lN+yOfI1Rb5osvBCiLtSdtiaHsmGnc540m +gwV5dOy0uaOXwTUA/RXaOYE6lTGQ3pfphqiZdwzlWqCE/xIWrG64jcN7ksKsLtB9KOy282A4aW8+ +2ARVPp7MVdK6/rtHBNcK2RYKNCn1WBPVT8+PVkuzHu7TmHnaCB4Mb7j4Fifvwm899qNLPg7kbWzb +O0ESm70NRyN/PErQr8Cv9u8btRXE64PECV90i9kR+8JWsTz4cMo0jUNAE4z9mQNUecYu6oah9jrU +Cbz0vGbMPVjQV0kK7iXiQe4T+Zs4NNEA9X7nlB38aQNiuJkFBT1reBK9sG9l +-----END CERTIFICATE----- + +TDC OCES Root CA +================ +-----BEGIN CERTIFICATE----- +MIIFGTCCBAGgAwIBAgIEPki9xDANBgkqhkiG9w0BAQUFADAxMQswCQYDVQQGEwJESzEMMAoGA1UE +ChMDVERDMRQwEgYDVQQDEwtUREMgT0NFUyBDQTAeFw0wMzAyMTEwODM5MzBaFw0zNzAyMTEwOTA5 +MzBaMDExCzAJBgNVBAYTAkRLMQwwCgYDVQQKEwNUREMxFDASBgNVBAMTC1REQyBPQ0VTIENBMIIB +IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArGL2YSCyz8DGhdfjeebM7fI5kqSXLmSjhFuH +nEz9pPPEXyG9VhDr2y5h7JNp46PMvZnDBfwGuMo2HP6QjklMxFaaL1a8z3sM8W9Hpg1DTeLpHTk0 +zY0s2RKY+ePhwUp8hjjEqcRhiNJerxomTdXkoCJHhNlktxmW/OwZ5LKXJk5KTMuPJItUGBxIYXvV +iGjaXbXqzRowwYCDdlCqT9HU3Tjw7xb04QxQBr/q+3pJoSgrHPb8FTKjdGqPqcNiKXEx5TukYBde +dObaE+3pHx8b0bJoc8YQNHVGEBDjkAB2QMuLt0MJIf+rTpPGWOmlgtt3xDqZsXKVSQTwtyv6e1mO +3QIDAQABo4ICNzCCAjMwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwgewGA1UdIASB +5DCB4TCB3gYIKoFQgSkBAQEwgdEwLwYIKwYBBQUHAgEWI2h0dHA6Ly93d3cuY2VydGlmaWthdC5k +ay9yZXBvc2l0b3J5MIGdBggrBgEFBQcCAjCBkDAKFgNUREMwAwIBARqBgUNlcnRpZmlrYXRlciBm +cmEgZGVubmUgQ0EgdWRzdGVkZXMgdW5kZXIgT0lEIDEuMi4yMDguMTY5LjEuMS4xLiBDZXJ0aWZp +Y2F0ZXMgZnJvbSB0aGlzIENBIGFyZSBpc3N1ZWQgdW5kZXIgT0lEIDEuMi4yMDguMTY5LjEuMS4x +LjARBglghkgBhvhCAQEEBAMCAAcwgYEGA1UdHwR6MHgwSKBGoESkQjBAMQswCQYDVQQGEwJESzEM +MAoGA1UEChMDVERDMRQwEgYDVQQDEwtUREMgT0NFUyBDQTENMAsGA1UEAxMEQ1JMMTAsoCqgKIYm +aHR0cDovL2NybC5vY2VzLmNlcnRpZmlrYXQuZGsvb2Nlcy5jcmwwKwYDVR0QBCQwIoAPMjAwMzAy +MTEwODM5MzBagQ8yMDM3MDIxMTA5MDkzMFowHwYDVR0jBBgwFoAUYLWF7FZkfhIZJ2cdUBVLc647 ++RIwHQYDVR0OBBYEFGC1hexWZH4SGSdnHVAVS3OuO/kSMB0GCSqGSIb2fQdBAAQQMA4bCFY2LjA6 +NC4wAwIEkDANBgkqhkiG9w0BAQUFAAOCAQEACromJkbTc6gJ82sLMJn9iuFXehHTuJTXCRBuo7E4 +A9G28kNBKWKnctj7fAXmMXAnVBhOinxO5dHKjHiIzxvTkIvmI/gLDjNDfZziChmPyQE+dF10yYsc +A+UYyAFMP8uXBV2YcaaYb7Z8vTd/vuGTJW1v8AqtFxjhA7wHKcitJuj4YfD9IQl+mo6paH1IYnK9 +AOoBmbgGglGBTvH1tJFUuSN6AJqfXY3gPGS5GhKSKseCRHI53OI8xthV9RVOyAUO28bQYqbsFbS1 +AoLbrIyigfCbmTH1ICCoiGEKB5+U/NDXG8wuF/MEJ3Zn61SD/aSQfgY9BKNDLdr8C2LqL19iUw== +-----END CERTIFICATE----- + +UTN DATACorp SGC Root CA +======================== +-----BEGIN CERTIFICATE----- +MIIEXjCCA0agAwIBAgIQRL4Mi1AAIbQR0ypoBqmtaTANBgkqhkiG9w0BAQUFADCBkzELMAkGA1UE +BhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhl +IFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xGzAZ +BgNVBAMTElVUTiAtIERBVEFDb3JwIFNHQzAeFw05OTA2MjQxODU3MjFaFw0xOTA2MjQxOTA2MzBa +MIGTMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4w +HAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cudXNlcnRy +dXN0LmNvbTEbMBkGA1UEAxMSVVROIC0gREFUQUNvcnAgU0dDMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEA3+5YEKIrblXEjr8uRgnn4AgPLit6E5Qbvfa2gI5lBZMAHryv4g+OGQ0SR+ys +raP6LnD43m77VkIVni5c7yPeIbkFdicZD0/Ww5y0vpQZY/KmEQrrU0icvvIpOxboGqBMpsn0GFlo +wHDyUwDAXlCCpVZvNvlK4ESGoE1O1kduSUrLZ9emxAW5jh70/P/N5zbgnAVssjMiFdC04MwXwLLA +9P4yPykqlXvY8qdOD1R8oQ2AswkDwf9c3V6aPryuvEeKaq5xyh+xKrhfQgUL7EYw0XILyulWbfXv +33i+Ybqypa4ETLyorGkVl73v67SMvzX41MPRKA5cOp9wGDMgd8SirwIDAQABo4GrMIGoMAsGA1Ud +DwQEAwIBxjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRTMtGzz3/64PGgXYVOktKeRR20TzA9 +BgNVHR8ENjA0MDKgMKAuhixodHRwOi8vY3JsLnVzZXJ0cnVzdC5jb20vVVROLURBVEFDb3JwU0dD +LmNybDAqBgNVHSUEIzAhBggrBgEFBQcDAQYKKwYBBAGCNwoDAwYJYIZIAYb4QgQBMA0GCSqGSIb3 +DQEBBQUAA4IBAQAnNZcAiosovcYzMB4p/OL31ZjUQLtgyr+rFywJNn9Q+kHcrpY6CiM+iVnJowft +Gzet/Hy+UUla3joKVAgWRcKZsYfNjGjgaQPpxE6YsjuMFrMOoAyYUJuTqXAJyCyjj98C5OBxOvG0 +I3KgqgHf35g+FFCgMSa9KOlaMCZ1+XtgHI3zzVAmbQQnmt/VDUVHKWss5nbZqSl9Mt3JNjy9rjXx +EZ4du5A/EkdOjtd+D2JzHVImOBwYSf0wdJrE5SIv2MCN7ZF6TACPcn9d2t0bi0Vr591pl6jFVkwP +DPafepE39peC4N1xaf92P2BNPM/3mfnGV/TJVTl4uix5yaaIK/QI +-----END CERTIFICATE----- + +UTN USERFirst Hardware Root CA +============================== +-----BEGIN CERTIFICATE----- +MIIEdDCCA1ygAwIBAgIQRL4Mi1AAJLQR0zYq/mUK/TANBgkqhkiG9w0BAQUFADCBlzELMAkGA1UE +BhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhl +IFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAd +BgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdhcmUwHhcNOTkwNzA5MTgxMDQyWhcNMTkwNzA5MTgx +OTIyWjCBlzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0 +eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVz +ZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdhcmUwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQCx98M4P7Sof885glFn0G2f0v9Y8+efK+wNiVSZuTiZFvfgIXlI +wrthdBKWHTxqctU8EGc6Oe0rE81m65UJM6Rsl7HoxuzBdXmcRl6Nq9Bq/bkqVRcQVLMZ8Jr28bFd +tqdt++BxF2uiiPsA3/4aMXcMmgF6sTLjKwEHOG7DpV4jvEWbe1DByTCP2+UretNb+zNAHqDVmBe8 +i4fDidNdoI6yqqr2jmmIBsX6iSHzCJ1pLgkzmykNRg+MzEk0sGlRvfkGzWitZky8PqxhvQqIDsjf +Pe58BEydCl5rkdbux+0ojatNh4lz0G6k0B4WixThdkQDf2Os5M1JnMWS9KsyoUhbAgMBAAGjgbkw +gbYwCwYDVR0PBAQDAgHGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFKFyXyYbKJhDlV0HN9WF +lp1L0sNFMEQGA1UdHwQ9MDswOaA3oDWGM2h0dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9VVE4tVVNF +UkZpcnN0LUhhcmR3YXJlLmNybDAxBgNVHSUEKjAoBggrBgEFBQcDAQYIKwYBBQUHAwUGCCsGAQUF +BwMGBggrBgEFBQcDBzANBgkqhkiG9w0BAQUFAAOCAQEARxkP3nTGmZev/K0oXnWO6y1n7k57K9cM +//bey1WiCuFMVGWTYGufEpytXoMs61quwOQt9ABjHbjAbPLPSbtNk28GpgoiskliCE7/yMgUsogW +XecB5BKV5UU0s4tpvc+0hY91UZ59Ojg6FEgSxvunOxqNDYJAB+gECJChicsZUN/KHAG8HQQZexB2 +lzvukJDKxA4fFm517zP4029bHpbj4HR3dHuKom4t3XbWOTCC8KucUvIqx69JXn7HaOWCgchqJ/kn +iCrVWFCVH/A7HFe7fRQ5YiuayZSSKqMiDP+JJn1fIytH1xUdqWqeUQ0qUZ6B+dQ7XnASfxAynB67 +nfhmqA== +-----END CERTIFICATE----- + +Camerfirma Chambers of Commerce Root +==================================== +-----BEGIN CERTIFICATE----- +MIIEvTCCA6WgAwIBAgIBADANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJFVTEnMCUGA1UEChMe +QUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1i +ZXJzaWduLm9yZzEiMCAGA1UEAxMZQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdDAeFw0wMzA5MzAx +NjEzNDNaFw0zNzA5MzAxNjEzNDRaMH8xCzAJBgNVBAYTAkVVMScwJQYDVQQKEx5BQyBDYW1lcmZp +cm1hIFNBIENJRiBBODI3NDMyODcxIzAhBgNVBAsTGmh0dHA6Ly93d3cuY2hhbWJlcnNpZ24ub3Jn +MSIwIAYDVQQDExlDaGFtYmVycyBvZiBDb21tZXJjZSBSb290MIIBIDANBgkqhkiG9w0BAQEFAAOC +AQ0AMIIBCAKCAQEAtzZV5aVdGDDg2olUkfzIx1L4L1DZ77F1c2VHfRtbunXF/KGIJPov7coISjlU +xFF6tdpg6jg8gbLL8bvZkSM/SAFwdakFKq0fcfPJVD0dBmpAPrMMhe5cG3nCYsS4No41XQEMIwRH +NaqbYE6gZj3LJgqcQKH0XZi/caulAGgq7YN6D6IUtdQis4CwPAxaUWktWBiP7Zme8a7ileb2R6jW +DA+wWFjbw2Y3npuRVDM30pQcakjJyfKl2qUMI/cjDpwyVV5xnIQFUZot/eZOKjRa3spAN2cMVCFV +d9oKDMyXroDclDZK9D7ONhMeU+SsTjoF7Nuucpw4i9A5O4kKPnf+dQIBA6OCAUQwggFAMBIGA1Ud +EwEB/wQIMAYBAf8CAQwwPAYDVR0fBDUwMzAxoC+gLYYraHR0cDovL2NybC5jaGFtYmVyc2lnbi5v +cmcvY2hhbWJlcnNyb290LmNybDAdBgNVHQ4EFgQU45T1sU3p26EpW1eLTXYGduHRooowDgYDVR0P +AQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzAnBgNVHREEIDAegRxjaGFtYmVyc3Jvb3RAY2hh +bWJlcnNpZ24ub3JnMCcGA1UdEgQgMB6BHGNoYW1iZXJzcm9vdEBjaGFtYmVyc2lnbi5vcmcwWAYD +VR0gBFEwTzBNBgsrBgEEAYGHLgoDATA+MDwGCCsGAQUFBwIBFjBodHRwOi8vY3BzLmNoYW1iZXJz +aWduLm9yZy9jcHMvY2hhbWJlcnNyb290Lmh0bWwwDQYJKoZIhvcNAQEFBQADggEBAAxBl8IahsAi +fJ/7kPMa0QOx7xP5IV8EnNrJpY0nbJaHkb5BkAFyk+cefV/2icZdp0AJPaxJRUXcLo0waLIJuvvD +L8y6C98/d3tGfToSJI6WjzwFCm/SlCgdbQzALogi1djPHRPH8EjX1wWnz8dHnjs8NMiAT9QUu/wN +UPf6s+xCX6ndbcj0dc97wXImsQEcXCz9ek60AcUFV7nnPKoF2YjpB0ZBzu9Bga5Y34OirsrXdx/n +ADydb47kMgkdTXg0eDQ8lJsm7U9xxhl6vSAiSFr+S30Dt+dYvsYyTnQeaN2oaFuzPu5ifdmA6Ap1 +erfutGWaIZDgqtCYvDi1czyL+Nw= +-----END CERTIFICATE----- + +Camerfirma Global Chambersign Root +================================== +-----BEGIN CERTIFICATE----- +MIIExTCCA62gAwIBAgIBADANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJFVTEnMCUGA1UEChMe +QUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1i +ZXJzaWduLm9yZzEgMB4GA1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwHhcNMDMwOTMwMTYx +NDE4WhcNMzcwOTMwMTYxNDE4WjB9MQswCQYDVQQGEwJFVTEnMCUGA1UEChMeQUMgQ2FtZXJmaXJt +YSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEg +MB4GA1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwggEgMA0GCSqGSIb3DQEBAQUAA4IBDQAw +ggEIAoIBAQCicKLQn0KuWxfH2H3PFIP8T8mhtxOviteePgQKkotgVvq0Mi+ITaFgCPS3CU6gSS9J +1tPfnZdan5QEcOw/Wdm3zGaLmFIoCQLfxS+EjXqXd7/sQJ0lcqu1PzKY+7e3/HKE5TWH+VX6ox8O +by4o3Wmg2UIQxvi1RMLQQ3/bvOSiPGpVeAp3qdjqGTK3L/5cPxvusZjsyq16aUXjlg9V9ubtdepl +6DJWk0aJqCWKZQbua795B9Dxt6/tLE2Su8CoX6dnfQTyFQhwrJLWfQTSM/tMtgsL+xrJxI0DqX5c +8lCrEqWhz0hQpe/SyBoT+rB/sYIcd2oPX9wLlY/vQ37mRQklAgEDo4IBUDCCAUwwEgYDVR0TAQH/ +BAgwBgEB/wIBDDA/BgNVHR8EODA2MDSgMqAwhi5odHRwOi8vY3JsLmNoYW1iZXJzaWduLm9yZy9j +aGFtYmVyc2lnbnJvb3QuY3JsMB0GA1UdDgQWBBRDnDafsJ4wTcbOX60Qq+UDpfqpFDAOBgNVHQ8B +Af8EBAMCAQYwEQYJYIZIAYb4QgEBBAQDAgAHMCoGA1UdEQQjMCGBH2NoYW1iZXJzaWducm9vdEBj +aGFtYmVyc2lnbi5vcmcwKgYDVR0SBCMwIYEfY2hhbWJlcnNpZ25yb290QGNoYW1iZXJzaWduLm9y +ZzBbBgNVHSAEVDBSMFAGCysGAQQBgYcuCgEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly9jcHMuY2hh +bWJlcnNpZ24ub3JnL2Nwcy9jaGFtYmVyc2lnbnJvb3QuaHRtbDANBgkqhkiG9w0BAQUFAAOCAQEA +PDtwkfkEVCeR4e3t/mh/YV3lQWVPMvEYBZRqHN4fcNs+ezICNLUMbKGKfKX0j//U2K0X1S0E0T9Y +gOKBWYi+wONGkyT+kL0mojAt6JcmVzWJdJYY9hXiryQZVgICsroPFOrGimbBhkVVi76SvpykBMdJ +PJ7oKXqJ1/6v/2j1pReQvayZzKWGVwlnRtvWFsJG8eSpUPWP0ZIV018+xgBJOm5YstHRJw0lyDL4 +IBHNfTIzSJRUTN3cecQwn+uOuFW114hcxWokPbLTBQNRxgfvzBRydD1ucs4YKIxKoHflCStFREes +t2d/AYoFWpO+ocH/+OcOZ6RHSXZddZAa9SaP8A== +-----END CERTIFICATE----- + +NetLock Notary (Class A) Root +============================= +-----BEGIN CERTIFICATE----- +MIIGfTCCBWWgAwIBAgICAQMwDQYJKoZIhvcNAQEEBQAwga8xCzAJBgNVBAYTAkhVMRAwDgYDVQQI +EwdIdW5nYXJ5MREwDwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6 +dG9uc2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0TG9j +ayBLb3pqZWd5em9pIChDbGFzcyBBKSBUYW51c2l0dmFueWtpYWRvMB4XDTk5MDIyNDIzMTQ0N1oX +DTE5MDIxOTIzMTQ0N1owga8xCzAJBgNVBAYTAkhVMRAwDgYDVQQIEwdIdW5nYXJ5MREwDwYDVQQH +EwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6dG9uc2FnaSBLZnQuMRowGAYD +VQQLExFUYW51c2l0dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0TG9jayBLb3pqZWd5em9pIChDbGFz +cyBBKSBUYW51c2l0dmFueWtpYWRvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvHSM +D7tM9DceqQWC2ObhbHDqeLVu0ThEDaiDzl3S1tWBxdRL51uUcCbbO51qTGL3cfNk1mE7PetzozfZ +z+qMkjvN9wfcZnSX9EUi3fRc4L9t875lM+QVOr/bmJBVOMTtplVjC7B4BPTjbsE/jvxReB+SnoPC +/tmwqcm8WgD/qaiYdPv2LD4VOQ22BFWoDpggQrOxJa1+mm9dU7GrDPzr4PN6s6iz/0b2Y6LYOph7 +tqyF/7AlT3Rj5xMHpQqPBffAZG9+pyeAlt7ULoZgx2srXnN7F+eRP2QM2EsiNCubMvJIH5+hCoR6 +4sKtlz2O1cH5VqNQ6ca0+pii7pXmKgOM3wIDAQABo4ICnzCCApswDgYDVR0PAQH/BAQDAgAGMBIG +A1UdEwEB/wQIMAYBAf8CAQQwEQYJYIZIAYb4QgEBBAQDAgAHMIICYAYJYIZIAYb4QgENBIICURaC +Ak1GSUdZRUxFTSEgRXplbiB0YW51c2l0dmFueSBhIE5ldExvY2sgS2Z0LiBBbHRhbGFub3MgU3pv +bGdhbHRhdGFzaSBGZWx0ZXRlbGVpYmVuIGxlaXJ0IGVsamFyYXNvayBhbGFwamFuIGtlc3p1bHQu +IEEgaGl0ZWxlc2l0ZXMgZm9seWFtYXRhdCBhIE5ldExvY2sgS2Z0LiB0ZXJtZWtmZWxlbG9zc2Vn +LWJpenRvc2l0YXNhIHZlZGkuIEEgZGlnaXRhbGlzIGFsYWlyYXMgZWxmb2dhZGFzYW5hayBmZWx0 +ZXRlbGUgYXogZWxvaXJ0IGVsbGVub3J6ZXNpIGVsamFyYXMgbWVndGV0ZWxlLiBBeiBlbGphcmFz +IGxlaXJhc2EgbWVndGFsYWxoYXRvIGEgTmV0TG9jayBLZnQuIEludGVybmV0IGhvbmxhcGphbiBh +IGh0dHBzOi8vd3d3Lm5ldGxvY2submV0L2RvY3MgY2ltZW4gdmFneSBrZXJoZXRvIGF6IGVsbGVu +b3J6ZXNAbmV0bG9jay5uZXQgZS1tYWlsIGNpbWVuLiBJTVBPUlRBTlQhIFRoZSBpc3N1YW5jZSBh +bmQgdGhlIHVzZSBvZiB0aGlzIGNlcnRpZmljYXRlIGlzIHN1YmplY3QgdG8gdGhlIE5ldExvY2sg +Q1BTIGF2YWlsYWJsZSBhdCBodHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIG9yIGJ5IGUtbWFp +bCBhdCBjcHNAbmV0bG9jay5uZXQuMA0GCSqGSIb3DQEBBAUAA4IBAQBIJEb3ulZv+sgoA0BO5TE5 +ayZrU3/b39/zcT0mwBQOxmd7I6gMc90Bu8bKbjc5VdXHjFYgDigKDtIqpLBJUsY4B/6+CgmM0ZjP +ytoUMaFP0jn8DxEsQ8Pdq5PHVT5HfBgaANzze9jyf1JsIPQLX2lS9O74silg6+NJMSEN1rUQQeJB +CWziGppWS3cC9qCbmieH6FUpccKQn0V4GuEVZD3QDtigdp+uxdAu6tYPVuxkf1qbFFgBJ34TUMdr +KuZoPL9coAob4Q566eKAw+np9v1sEZ7Q5SgnK1QyQhSCdeZK8CtmdWOMovsEPoMOmzbwGOQmIMOM +8CgHrTwXZoi1/baI +-----END CERTIFICATE----- + +NetLock Business (Class B) Root +=============================== +-----BEGIN CERTIFICATE----- +MIIFSzCCBLSgAwIBAgIBaTANBgkqhkiG9w0BAQQFADCBmTELMAkGA1UEBhMCSFUxETAPBgNVBAcT +CEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0b25zYWdpIEtmdC4xGjAYBgNV +BAsTEVRhbnVzaXR2YW55a2lhZG9rMTIwMAYDVQQDEylOZXRMb2NrIFV6bGV0aSAoQ2xhc3MgQikg +VGFudXNpdHZhbnlraWFkbzAeFw05OTAyMjUxNDEwMjJaFw0xOTAyMjAxNDEwMjJaMIGZMQswCQYD +VQQGEwJIVTERMA8GA1UEBxMIQnVkYXBlc3QxJzAlBgNVBAoTHk5ldExvY2sgSGFsb3phdGJpenRv +bnNhZ2kgS2Z0LjEaMBgGA1UECxMRVGFudXNpdHZhbnlraWFkb2sxMjAwBgNVBAMTKU5ldExvY2sg +VXpsZXRpIChDbGFzcyBCKSBUYW51c2l0dmFueWtpYWRvMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB +iQKBgQCx6gTsIKAjwo84YM/HRrPVG/77uZmeBNwcf4xKgZjupNTKihe5In+DCnVMm8Bp2GQ5o+2S +o/1bXHQawEfKOml2mrriRBf8TKPV/riXiK+IA4kfpPIEPsgHC+b5sy96YhQJRhTKZPWLgLViqNhr +1nGTLbO/CVRY7QbrqHvcQ7GhaQIDAQABo4ICnzCCApswEgYDVR0TAQH/BAgwBgEB/wIBBDAOBgNV +HQ8BAf8EBAMCAAYwEQYJYIZIAYb4QgEBBAQDAgAHMIICYAYJYIZIAYb4QgENBIICURaCAk1GSUdZ +RUxFTSEgRXplbiB0YW51c2l0dmFueSBhIE5ldExvY2sgS2Z0LiBBbHRhbGFub3MgU3pvbGdhbHRh +dGFzaSBGZWx0ZXRlbGVpYmVuIGxlaXJ0IGVsamFyYXNvayBhbGFwamFuIGtlc3p1bHQuIEEgaGl0 +ZWxlc2l0ZXMgZm9seWFtYXRhdCBhIE5ldExvY2sgS2Z0LiB0ZXJtZWtmZWxlbG9zc2VnLWJpenRv +c2l0YXNhIHZlZGkuIEEgZGlnaXRhbGlzIGFsYWlyYXMgZWxmb2dhZGFzYW5hayBmZWx0ZXRlbGUg +YXogZWxvaXJ0IGVsbGVub3J6ZXNpIGVsamFyYXMgbWVndGV0ZWxlLiBBeiBlbGphcmFzIGxlaXJh +c2EgbWVndGFsYWxoYXRvIGEgTmV0TG9jayBLZnQuIEludGVybmV0IGhvbmxhcGphbiBhIGh0dHBz +Oi8vd3d3Lm5ldGxvY2submV0L2RvY3MgY2ltZW4gdmFneSBrZXJoZXRvIGF6IGVsbGVub3J6ZXNA +bmV0bG9jay5uZXQgZS1tYWlsIGNpbWVuLiBJTVBPUlRBTlQhIFRoZSBpc3N1YW5jZSBhbmQgdGhl +IHVzZSBvZiB0aGlzIGNlcnRpZmljYXRlIGlzIHN1YmplY3QgdG8gdGhlIE5ldExvY2sgQ1BTIGF2 +YWlsYWJsZSBhdCBodHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIG9yIGJ5IGUtbWFpbCBhdCBj +cHNAbmV0bG9jay5uZXQuMA0GCSqGSIb3DQEBBAUAA4GBAATbrowXr/gOkDFOzT4JwG06sPgzTEdM +43WIEJessDgVkcYplswhwG08pXTP2IKlOcNl40JwuyKQ433bNXbhoLXan3BukxowOR0w2y7jfLKR +stE3Kfq51hdcR0/jHTjrn9V7lagonhVK0dHQKwCXoOKSNitjrFgBazMpUIaD8QFI +-----END CERTIFICATE----- + +NetLock Express (Class C) Root +============================== +-----BEGIN CERTIFICATE----- +MIIFTzCCBLigAwIBAgIBaDANBgkqhkiG9w0BAQQFADCBmzELMAkGA1UEBhMCSFUxETAPBgNVBAcT +CEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0b25zYWdpIEtmdC4xGjAYBgNV +BAsTEVRhbnVzaXR2YW55a2lhZG9rMTQwMgYDVQQDEytOZXRMb2NrIEV4cHJlc3N6IChDbGFzcyBD +KSBUYW51c2l0dmFueWtpYWRvMB4XDTk5MDIyNTE0MDgxMVoXDTE5MDIyMDE0MDgxMVowgZsxCzAJ +BgNVBAYTAkhVMREwDwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6 +dG9uc2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE0MDIGA1UEAxMrTmV0TG9j +ayBFeHByZXNzeiAoQ2xhc3MgQykgVGFudXNpdHZhbnlraWFkbzCBnzANBgkqhkiG9w0BAQEFAAOB +jQAwgYkCgYEA6+ywbGGKIyWvYCDj2Z/8kwvbXY2wobNAOoLO/XXgeDIDhlqGlZHtU/qdQPzm6N3Z +W3oDvV3zOwzDUXmbrVWg6dADEK8KuhRC2VImESLH0iDMgqSaqf64gXadarfSNnU+sYYJ9m5tfk63 +euyucYT2BDMIJTLrdKwWRMbkQJMdf60CAwEAAaOCAp8wggKbMBIGA1UdEwEB/wQIMAYBAf8CAQQw +DgYDVR0PAQH/BAQDAgAGMBEGCWCGSAGG+EIBAQQEAwIABzCCAmAGCWCGSAGG+EIBDQSCAlEWggJN +RklHWUVMRU0hIEV6ZW4gdGFudXNpdHZhbnkgYSBOZXRMb2NrIEtmdC4gQWx0YWxhbm9zIFN6b2xn +YWx0YXRhc2kgRmVsdGV0ZWxlaWJlbiBsZWlydCBlbGphcmFzb2sgYWxhcGphbiBrZXN6dWx0LiBB +IGhpdGVsZXNpdGVzIGZvbHlhbWF0YXQgYSBOZXRMb2NrIEtmdC4gdGVybWVrZmVsZWxvc3NlZy1i +aXp0b3NpdGFzYSB2ZWRpLiBBIGRpZ2l0YWxpcyBhbGFpcmFzIGVsZm9nYWRhc2FuYWsgZmVsdGV0 +ZWxlIGF6IGVsb2lydCBlbGxlbm9yemVzaSBlbGphcmFzIG1lZ3RldGVsZS4gQXogZWxqYXJhcyBs +ZWlyYXNhIG1lZ3RhbGFsaGF0byBhIE5ldExvY2sgS2Z0LiBJbnRlcm5ldCBob25sYXBqYW4gYSBo +dHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIGNpbWVuIHZhZ3kga2VyaGV0byBheiBlbGxlbm9y +emVzQG5ldGxvY2submV0IGUtbWFpbCBjaW1lbi4gSU1QT1JUQU5UISBUaGUgaXNzdWFuY2UgYW5k +IHRoZSB1c2Ugb2YgdGhpcyBjZXJ0aWZpY2F0ZSBpcyBzdWJqZWN0IHRvIHRoZSBOZXRMb2NrIENQ +UyBhdmFpbGFibGUgYXQgaHR0cHM6Ly93d3cubmV0bG9jay5uZXQvZG9jcyBvciBieSBlLW1haWwg +YXQgY3BzQG5ldGxvY2submV0LjANBgkqhkiG9w0BAQQFAAOBgQAQrX/XDDKACtiG8XmYta3UzbM2 +xJZIwVzNmtkFLp++UOv0JhQQLdRmF/iewSf98e3ke0ugbLWrmldwpu2gpO0u9f38vf5NNwgMvOOW +gyL1SRt/Syu0VMGAfJlOHdCM7tCs5ZL6dVb+ZKATj7i4Fp1hBWeAyNDYpQcCNJgEjTME1A== +-----END CERTIFICATE----- + +XRamp Global CA Root +==================== +-----BEGIN CERTIFICATE----- +MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCBgjELMAkGA1UE +BhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2Vj +dXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwHhcNMDQxMTAxMTcxNDA0WhcNMzUwMTAxMDUzNzE5WjCBgjELMAkGA1UEBhMCVVMx +HjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2VjdXJpdHkg +U2VydmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYJB69FbS638eMpSe2OAtp87ZOqCwu +IR1cRN8hXX4jdP5efrRKt6atH67gBhbim1vZZ3RrXYCPKZ2GG9mcDZhtdhAoWORlsH9KmHmf4MMx +foArtYzAQDsRhtDLooY2YKTVMIJt2W7QDxIEM5dfT2Fa8OT5kavnHTu86M/0ay00fOJIYRyO82FE +zG+gSqmUsE3a56k0enI4qEHMPJQRfevIpoy3hsvKMzvZPTeL+3o+hiznc9cKV6xkmxnr9A8ECIqs +AxcZZPRaJSKNNCyy9mgdEm3Tih4U2sSPpuIjhdV6Db1q4Ons7Be7QhtnqiXtRYMh/MHJfNViPvry +xS3T/dRlAgMBAAGjgZ8wgZwwEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud +EwEB/wQFMAMBAf8wHQYDVR0OBBYEFMZPoj0GY4QJnM5i5ASsjVy16bYbMDYGA1UdHwQvMC0wK6Ap +oCeGJWh0dHA6Ly9jcmwueHJhbXBzZWN1cml0eS5jb20vWEdDQS5jcmwwEAYJKwYBBAGCNxUBBAMC +AQEwDQYJKoZIhvcNAQEFBQADggEBAJEVOQMBG2f7Shz5CmBbodpNl2L5JFMn14JkTpAuw0kbK5rc +/Kh4ZzXxHfARvbdI4xD2Dd8/0sm2qlWkSLoC295ZLhVbO50WfUfXN+pfTXYSNrsf16GBBEYgoyxt +qZ4Bfj8pzgCT3/3JknOJiWSe5yvkHJEs0rnOfc5vMZnT5r7SHpDwCRR5XCOrTdLaIR9NmXmd4c8n +nxCbHIgNsIpkQTG4DmyQJKSbXHGPurt+HBvbaoAPIbzp26a3QPSyi6mx5O+aGtA9aZnuqCij4Tyz +8LIRnM98QObd50N9otg6tamN8jSZxNQQ4Qb9CYQQO+7ETPTsJ3xCwnR8gooJybQDJbw= +-----END CERTIFICATE----- + +Go Daddy Class 2 CA +=================== +-----BEGIN CERTIFICATE----- +MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMY +VGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkG +A1UEBhMCVVMxITAfBgNVBAoTGFRoZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28g +RGFkZHkgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQAD +ggENADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCAPVYYYwhv +2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6wwdhFJ2+qN1j3hybX2C32 +qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXiEqITLdiOr18SPaAIBQi2XKVlOARFmR6j +YGB0xUGlcmIbYsUfb18aQr4CUWWoriMYavx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmY +vLEHZ6IVDd2gWMZEewo+YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0O +BBYEFNLEsNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h/t2o +atTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMu +MTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwG +A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wim +PQoZ+YeAEW5p5JYXMP80kWNyOO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKt +I3lpjbi2Tc7PTMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ +HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mERdEr/VxqHD3VI +Ls9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5CufReYNnyicsbkqWletNw+vHX/b +vZ8= +-----END CERTIFICATE----- + +Starfield Class 2 CA +==================== +-----BEGIN CERTIFICATE----- +MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzElMCMGA1UEChMc +U3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZpZWxkIENsYXNzIDIg +Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQwNjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBo +MQswCQYDVQQGEwJVUzElMCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAG +A1UECxMpU3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqG +SIb3DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf8MOh2tTY +bitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN+lq2cwQlZut3f+dZxkqZ +JRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVm +epsZGD3/cVE8MC5fvj13c7JdBmzDI1aaK4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSN +F4Azbl5KXZnJHoe0nRrA1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HF +MIHCMB0GA1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fRzt0f +hvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNo +bm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBDbGFzcyAyIENlcnRpZmljYXRpb24g +QXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGs +afPzWdqbAYcaT1epoXkJKtv3L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLM +PUxA2IGvd56Deruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl +xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynpVSJYACPq4xJD +KVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEYWQPJIrSPnNVeKtelttQKbfi3 +QBFGmh95DmK/D5fs4C8fF5Q= +-----END CERTIFICATE----- + +StartCom Certification Authority +================================ +-----BEGIN CERTIFICATE----- +MIIHyTCCBbGgAwIBAgIBATANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMN +U3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmlu +ZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0 +NjM2WhcNMzYwOTE3MTk0NjM2WjB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRk +LjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMg +U3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw +ggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZkpMyONvg45iPwbm2xPN1y +o4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rfOQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/ +Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/CJi/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/d +eMotHweXMAEtcnn6RtYTKqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt +2PZE4XNiHzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMMAv+Z +6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w+2OqqGwaVLRcJXrJ +osmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/ +untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVc +UjyJthkqcwEKDwOzEmDyei+B26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT +37uMdBNSSwIDAQABo4ICUjCCAk4wDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAa4wHQYDVR0OBBYE +FE4L7xqkQFulF2mHMMo0aEPQQa7yMGQGA1UdHwRdMFswLKAqoCiGJmh0dHA6Ly9jZXJ0LnN0YXJ0 +Y29tLm9yZy9zZnNjYS1jcmwuY3JsMCugKaAnhiVodHRwOi8vY3JsLnN0YXJ0Y29tLm9yZy9zZnNj +YS1jcmwuY3JsMIIBXQYDVR0gBIIBVDCCAVAwggFMBgsrBgEEAYG1NwEBATCCATswLwYIKwYBBQUH +AgEWI2h0dHA6Ly9jZXJ0LnN0YXJ0Y29tLm9yZy9wb2xpY3kucGRmMDUGCCsGAQUFBwIBFilodHRw +Oi8vY2VydC5zdGFydGNvbS5vcmcvaW50ZXJtZWRpYXRlLnBkZjCB0AYIKwYBBQUHAgIwgcMwJxYg +U3RhcnQgQ29tbWVyY2lhbCAoU3RhcnRDb20pIEx0ZC4wAwIBARqBl0xpbWl0ZWQgTGlhYmlsaXR5 +LCByZWFkIHRoZSBzZWN0aW9uICpMZWdhbCBMaW1pdGF0aW9ucyogb2YgdGhlIFN0YXJ0Q29tIENl +cnRpZmljYXRpb24gQXV0aG9yaXR5IFBvbGljeSBhdmFpbGFibGUgYXQgaHR0cDovL2NlcnQuc3Rh +cnRjb20ub3JnL3BvbGljeS5wZGYwEQYJYIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilT +dGFydENvbSBGcmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQUFAAOC +AgEAFmyZ9GYMNPXQhV59CuzaEE44HF7fpiUFS5Eyweg78T3dRAlbB0mKKctmArexmvclmAk8jhvh +3TaHK0u7aNM5Zj2gJsfyOZEdUauCe37Vzlrk4gNXcGmXCPleWKYK34wGmkUWFjgKXlf2Ysd6AgXm +vB618p70qSmD+LIU424oh0TDkBreOKk8rENNZEXO3SipXPJzewT4F+irsfMuXGRuczE6Eri8sxHk +fY+BUZo7jYn0TZNmezwD7dOaHZrzZVD1oNB1ny+v8OqCQ5j4aZyJecRDjkZy42Q2Eq/3JR44iZB3 +fsNrarnDy0RLrHiQi+fHLB5LEUTINFInzQpdn4XBidUaePKVEFMy3YCEZnXZtWgo+2EuvoSoOMCZ +EoalHmdkrQYuL6lwhceWD3yJZfWOQ1QOq92lgDmUYMA0yZZwLKMS9R9Ie70cfmu3nZD0Ijuu+Pwq +yvqCUqDvr0tVk+vBtfAii6w0TiYiBKGHLHVKt+V9E9e4DGTANtLJL4YSjCMJwRuCO3NJo2pXh5Tl +1njFmUNj403gdy3hZZlyaQQaRwnmDwFWJPsfvw55qVguucQJAX6Vum0ABj6y6koQOdjQK/W/7HW/ +lwLFCRsI3FU34oH7N4RDYiDK51ZLZer+bMEkkyShNOsF/5oirpt9P/FlUQqmMGqz9IgcgA38coro +g14= +-----END CERTIFICATE----- + +Taiwan GRCA +=========== +-----BEGIN CERTIFICATE----- +MIIFcjCCA1qgAwIBAgIQH51ZWtcvwgZEpYAIaeNe9jANBgkqhkiG9w0BAQUFADA/MQswCQYDVQQG +EwJUVzEwMC4GA1UECgwnR292ZXJubWVudCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4X +DTAyMTIwNTEzMjMzM1oXDTMyMTIwNTEzMjMzM1owPzELMAkGA1UEBhMCVFcxMDAuBgNVBAoMJ0dv +dmVybm1lbnQgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBAJoluOzMonWoe/fOW1mKydGGEghU7Jzy50b2iPN86aXfTEc2pBsBHH8eV4qN +w8XRIePaJD9IK/ufLqGU5ywck9G/GwGHU5nOp/UKIXZ3/6m3xnOUT0b3EEk3+qhZSV1qgQdW8or5 +BtD3cCJNtLdBuTK4sfCxw5w/cP1T3YGq2GN49thTbqGsaoQkclSGxtKyyhwOeYHWtXBiCAEuTk8O +1RGvqa/lmr/czIdtJuTJV6L7lvnM4T9TjGxMfptTCAtsF/tnyMKtsc2AtJfcdgEWFelq16TheEfO +htX7MfP6Mb40qij7cEwdScevLJ1tZqa2jWR+tSBqnTuBto9AAGdLiYa4zGX+FVPpBMHWXx1E1wov +J5pGfaENda1UhhXcSTvxls4Pm6Dso3pdvtUqdULle96ltqqvKKyskKw4t9VoNSZ63Pc78/1Fm9G7 +Q3hub/FCVGqY8A2tl+lSXunVanLeavcbYBT0peS2cWeqH+riTcFCQP5nRhc4L0c/cZyu5SHKYS1t +B6iEfC3uUSXxY5Ce/eFXiGvviiNtsea9P63RPZYLhY3Naye7twWb7LuRqQoHEgKXTiCQ8P8NHuJB +O9NAOueNXdpm5AKwB1KYXA6OM5zCppX7VRluTI6uSw+9wThNXo+EHWbNxWCWtFJaBYmOlXqYwZE8 +lSOyDvR5tMl8wUohAgMBAAGjajBoMB0GA1UdDgQWBBTMzO/MKWCkO7GStjz6MmKPrCUVOzAMBgNV +HRMEBTADAQH/MDkGBGcqBwAEMTAvMC0CAQAwCQYFKw4DAhoFADAHBgVnKgMAAAQUA5vwIhP/lSg2 +09yewDL7MTqKUWUwDQYJKoZIhvcNAQEFBQADggIBAECASvomyc5eMN1PhnR2WPWus4MzeKR6dBcZ +TulStbngCnRiqmjKeKBMmo4sIy7VahIkv9Ro04rQ2JyftB8M3jh+Vzj8jeJPXgyfqzvS/3WXy6Tj +Zwj/5cAWtUgBfen5Cv8b5Wppv3ghqMKnI6mGq3ZW6A4M9hPdKmaKZEk9GhiHkASfQlK3T8v+R0F2 +Ne//AHY2RTKbxkaFXeIksB7jSJaYV0eUVXoPQbFEJPPB/hprv4j9wabak2BegUqZIJxIZhm1AHlU +D7gsL0u8qV1bYH+Mh6XgUmMqvtg7hUAV/h62ZT/FS9p+tXo1KaMuephgIqP0fSdOLeq0dDzpD6Qz +DxARvBMB1uUO07+1EqLhRSPAzAhuYbeJq4PjJB7mXQfnHyA+z2fI56wwbSdLaG5LKlwCCDTb+Hbk +Z6MmnD+iMsJKxYEYMRBWqoTvLQr/uB930r+lWKBi5NdLkXWNiYCYfm3LU05er/ayl4WXudpVBrkk +7tfGOB5jGxI7leFYrPLfhNVfmS8NVVvmONsuP3LpSIXLuykTjx44VbnzssQwmSNOXfJIoRIM3BKQ +CZBUkQM8R+XVyWXgt0t97EfTsws+rZ7QdAAO671RrcDeLMDDav7v3Aun+kbfYNucpllQdSNpc5Oy ++fwC00fmcc4QAu4njIT/rEUNE1yDMuAlpYYsfPQS +-----END CERTIFICATE----- + +Firmaprofesional Root CA +======================== +-----BEGIN CERTIFICATE----- +MIIEVzCCAz+gAwIBAgIBATANBgkqhkiG9w0BAQUFADCBnTELMAkGA1UEBhMCRVMxIjAgBgNVBAcT +GUMvIE11bnRhbmVyIDI0NCBCYXJjZWxvbmExQjBABgNVBAMTOUF1dG9yaWRhZCBkZSBDZXJ0aWZp +Y2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2ODEmMCQGCSqGSIb3DQEJARYXY2FA +ZmlybWFwcm9mZXNpb25hbC5jb20wHhcNMDExMDI0MjIwMDAwWhcNMTMxMDI0MjIwMDAwWjCBnTEL +MAkGA1UEBhMCRVMxIjAgBgNVBAcTGUMvIE11bnRhbmVyIDI0NCBCYXJjZWxvbmExQjBABgNVBAMT +OUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2 +ODEmMCQGCSqGSIb3DQEJARYXY2FAZmlybWFwcm9mZXNpb25hbC5jb20wggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQDnIwNvbyOlXnjOlSztlB5uCp4Bx+ow0Syd3Tfom5h5VtP8c9/Qit5V +j1H5WuretXDE7aTt/6MNbg9kUDGvASdYrv5sp0ovFy3Tc9UTHI9ZpTQsHVQERc1ouKDAA6XPhUJH +lShbz++AbOCQl4oBPB3zhxAwJkh91/zpnZFx/0GaqUC1N5wpIE8fUuOgfRNtVLcK3ulqTgesrBlf +3H5idPayBQC6haD9HThuy1q7hryUZzM1gywfI834yJFxzJeL764P3CkDG8A563DtwW4O2GcLiam8 +NeTvtjS0pbbELaW+0MOUJEjb35bTALVmGotmBQ/dPz/LP6pemkr4tErvlTcbAgMBAAGjgZ8wgZww +KgYDVR0RBCMwIYYfaHR0cDovL3d3dy5maXJtYXByb2Zlc2lvbmFsLmNvbTASBgNVHRMBAf8ECDAG +AQH/AgEBMCsGA1UdEAQkMCKADzIwMDExMDI0MjIwMDAwWoEPMjAxMzEwMjQyMjAwMDBaMA4GA1Ud +DwEB/wQEAwIBBjAdBgNVHQ4EFgQUMwugZtHq2s7eYpMEKFK1FH84aLcwDQYJKoZIhvcNAQEFBQAD +ggEBAEdz/o0nVPD11HecJ3lXV7cVVuzH2Fi3AQL0M+2TUIiefEaxvT8Ub/GzR0iLjJcG1+p+o1wq +u00vR+L4OQbJnC4xGgN49Lw4xiKLMzHwFgQEffl25EvXwOaD7FnMP97/T2u3Z36mhoEyIwOdyPdf +wUpgpZKpsaSgYMN4h7Mi8yrrW6ntBas3D7Hi05V2Y1Z0jFhyGzflZKG+TQyTmAyX9odtsz/ny4Cm +7YjHX1BiAuiZdBbQ5rQ58SfLyEDW44YQqSMSkuBpQWOnryULwMWSyx6Yo1q6xTMPoJcB3X/ge9YG +VM+h4k0460tQtcsm9MracEpqoeJ5quGnM/b9Sh/22WA= +-----END CERTIFICATE----- + +Wells Fargo Root CA +=================== +-----BEGIN CERTIFICATE----- +MIID5TCCAs2gAwIBAgIEOeSXnjANBgkqhkiG9w0BAQUFADCBgjELMAkGA1UEBhMCVVMxFDASBgNV +BAoTC1dlbGxzIEZhcmdvMSwwKgYDVQQLEyNXZWxscyBGYXJnbyBDZXJ0aWZpY2F0aW9uIEF1dGhv +cml0eTEvMC0GA1UEAxMmV2VsbHMgRmFyZ28gUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcN +MDAxMDExMTY0MTI4WhcNMjEwMTE0MTY0MTI4WjCBgjELMAkGA1UEBhMCVVMxFDASBgNVBAoTC1dl +bGxzIEZhcmdvMSwwKgYDVQQLEyNXZWxscyBGYXJnbyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEv +MC0GA1UEAxMmV2VsbHMgRmFyZ28gUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDVqDM7Jvk0/82bfuUER84A4n135zHCLielTWi5MbqNQ1mX +x3Oqfz1cQJ4F5aHiidlMuD+b+Qy0yGIZLEWukR5zcUHESxP9cMIlrCL1dQu3U+SlK93OvRw6esP3 +E48mVJwWa2uv+9iWsWCaSOAlIiR5NM4OJgALTqv9i86C1y8IcGjBqAr5dE8Hq6T54oN+J3N0Prj5 +OEL8pahbSCOz6+MlsoCultQKnMJ4msZoGK43YjdeUXWoWGPAUe5AeH6orxqg4bB4nVCMe+ez/I4j +sNtlAHCEAQgAFG5Uhpq6zPk3EPbg3oQtnaSFN9OH4xXQwReQfhkhahKpdv0SAulPIV4XAgMBAAGj +YTBfMA8GA1UdEwEB/wQFMAMBAf8wTAYDVR0gBEUwQzBBBgtghkgBhvt7hwcBCzAyMDAGCCsGAQUF +BwIBFiRodHRwOi8vd3d3LndlbGxzZmFyZ28uY29tL2NlcnRwb2xpY3kwDQYJKoZIhvcNAQEFBQAD +ggEBANIn3ZwKdyu7IvICtUpKkfnRLb7kuxpo7w6kAOnu5+/u9vnldKTC2FJYxHT7zmu1Oyl5GFrv +m+0fazbuSCUlFLZWohDo7qd/0D+j0MNdJu4HzMPBJCGHHt8qElNvQRbn7a6U+oxy+hNH8Dx+rn0R +OhPs7fpvcmR7nX1/Jv16+yWt6j4pf0zjAFcysLPp7VMX2YuyFA4w6OXVE8Zkr8QA1dhYJPz1j+zx +x32l2w8n0cbyQIjmH/ZhqPRCyLk306m+LFZ4wnKbWV01QIroTmMatukgalHizqSQ33ZwmVxwQ023 +tqcZZE6St8WRPH9IFmV7Fv3L/PvZ1dZPIWU7Sn9Ho/s= +-----END CERTIFICATE----- + +Swisscom Root CA 1 +================== +-----BEGIN CERTIFICATE----- +MIIF2TCCA8GgAwIBAgIQXAuFXAvnWUHfV8w/f52oNjANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQG +EwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0YWwgQ2VydGlmaWNhdGUgU2Vy +dmljZXMxGzAZBgNVBAMTElN3aXNzY29tIFJvb3QgQ0EgMTAeFw0wNTA4MTgxMjA2MjBaFw0yNTA4 +MTgyMjA2MjBaMGQxCzAJBgNVBAYTAmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGln +aXRhbCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAxMIIC +IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0LmwqAzZuz8h+BvVM5OAFmUgdbI9m2BtRsiM +MW8Xw/qabFbtPMWRV8PNq5ZJkCoZSx6jbVfd8StiKHVFXqrWW/oLJdihFvkcxC7mlSpnzNApbjyF +NDhhSbEAn9Y6cV9Nbc5fuankiX9qUvrKm/LcqfmdmUc/TilftKaNXXsLmREDA/7n29uj/x2lzZAe +AR81sH8A25Bvxn570e56eqeqDFdvpG3FEzuwpdntMhy0XmeLVNxzh+XTF3xmUHJd1BpYwdnP2IkC +b6dJtDZd0KTeByy2dbcokdaXvij1mB7qWybJvbCXc9qukSbraMH5ORXWZ0sKbU/Lz7DkQnGMU3nn +7uHbHaBuHYwadzVcFh4rUx80i9Fs/PJnB3r1re3WmquhsUvhzDdf/X/NTa64H5xD+SpYVUNFvJbN +cA78yeNmuk6NO4HLFWR7uZToXTNShXEuT46iBhFRyePLoW4xCGQMwtI89Tbo19AOeCMgkckkKmUp +WyL3Ic6DXqTz3kvTaI9GdVyDCW4pa8RwjPWd1yAv/0bSKzjCL3UcPX7ape8eYIVpQtPM+GP+HkM5 +haa2Y0EQs3MevNP6yn0WR+Kn1dCjigoIlmJWbjTb2QK5MHXjBNLnj8KwEUAKrNVxAmKLMb7dxiNY +MUJDLXT5xp6mig/p/r+D5kNXJLrvRjSq1xIBOO0CAwEAAaOBhjCBgzAOBgNVHQ8BAf8EBAMCAYYw +HQYDVR0hBBYwFDASBgdghXQBUwABBgdghXQBUwABMBIGA1UdEwEB/wQIMAYBAf8CAQcwHwYDVR0j +BBgwFoAUAyUv3m+CATpcLNwroWm1Z9SM0/0wHQYDVR0OBBYEFAMlL95vggE6XCzcK6FptWfUjNP9 +MA0GCSqGSIb3DQEBBQUAA4ICAQA1EMvspgQNDQ/NwNurqPKIlwzfky9NfEBWMXrrpA9gzXrzvsMn +jgM+pN0S734edAY8PzHyHHuRMSG08NBsl9Tpl7IkVh5WwzW9iAUPWxAaZOHHgjD5Mq2eUCzneAXQ +MbFamIp1TpBcahQq4FJHgmDmHtqBsfsUC1rxn9KVuj7QG9YVHaO+htXbD8BJZLsuUBlL0iT43R4H +VtA4oJVwIHaM190e3p9xxCPvgxNcoyQVTSlAPGrEqdi3pkSlDfTgnXceQHAm/NrZNuR55LU/vJtl +vrsRls/bxig5OgjOR1tTWsWZ/l2p3e9M1MalrQLmjAcSHm8D0W+go/MpvRLHUKKwf4ipmXeascCl +OS5cfGniLLDqN2qk4Vrh9VDlg++luyqI54zb/W1elxmofmZ1a3Hqv7HHb6D0jqTsNFFbjCYDcKF3 +1QESVwA12yPeDooomf2xEG9L/zgtYE4snOtnta1J7ksfrK/7DZBaZmBwXarNeNQk7shBoJMBkpxq +nvy5JMWzFYJ+vq6VK+uxwNrjAWALXmmshFZhvnEX/h0TD/7Gh0Xp/jKgGg0TpJRVcaUWi7rKibCy +x/yP2FS1k2Kdzs9Z+z0YzirLNRWCXf9UIltxUvu3yf5gmwBBZPCqKuy2QkPOiWaByIufOVQDJdMW +NY6E0F/6MBr1mmz0DlP5OlvRHA== +-----END CERTIFICATE----- + +DigiCert Assured ID Root CA +=========================== +-----BEGIN CERTIFICATE----- +MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQw +IgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzEx +MTEwMDAwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL +ExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0Ew +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7cJpSIqvTO +9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYPmDI2dsze3Tyoou9q+yHy +UmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW +/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpy +oeb6pNnVFzF1roV9Iq4/AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whf +GHdPAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRF +66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzANBgkq +hkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRCdWKuh+vy1dneVrOfzM4UKLkNl2Bc +EkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTffwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38Fn +SbNd67IJKusm7Xi+fT8r87cmNW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i +8b5QZ7dsvfPxH2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe ++o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g== +-----END CERTIFICATE----- + +DigiCert Global Root CA +======================= +-----BEGIN CERTIFICATE----- +MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBhMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAw +HgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBDQTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAw +MDAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3 +dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkq +hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsBCSDMAZOn +TjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97nh6Vfe63SKMI2tavegw5 +BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt43C/dxC//AH2hdmoRBBYMql1GNXRor5H +4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7PT19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y +7vrTC0LUq7dBMtoM1O/4gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQAB +o2MwYTAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbRTLtm +8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDQYJKoZIhvcNAQEF +BQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/EsrhMAtudXH/vTBH1jLuG2cenTnmCmr +EbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIt +tep3Sp+dWOIrWcBAI+0tKIJFPnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886 +UAb3LujEV0lsYSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk +CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= +-----END CERTIFICATE----- + +DigiCert High Assurance EV Root CA +================================== +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBsMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSsw +KQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5jZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAw +MFoXDTMxMTExMDAwMDAwMFowbDELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZ +MBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFu +Y2UgRVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm+9S75S0t +Mqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTWPNt0OKRKzE0lgvdKpVMS +OO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEMxChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3 +MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFBIk5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQ +NAQTXKFx01p8VdteZOE3hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUe +h10aUAsgEsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMB +Af8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaAFLE+w2kD+L9HAdSY +JhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3NecnzyIZgYIVyHbIUf4KmeqvxgydkAQ +V8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6zeM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFp +myPInngiK3BD41VHMWEZ71jFhS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkK +mNEVX58Svnw2Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe +vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep+OkuE6N36B9K +-----END CERTIFICATE----- + +Certplus Class 2 Primary CA +=========================== +-----BEGIN CERTIFICATE----- +MIIDkjCCAnqgAwIBAgIRAIW9S/PY2uNp9pTXX8OlRCMwDQYJKoZIhvcNAQEFBQAwPTELMAkGA1UE +BhMCRlIxETAPBgNVBAoTCENlcnRwbHVzMRswGQYDVQQDExJDbGFzcyAyIFByaW1hcnkgQ0EwHhcN +OTkwNzA3MTcwNTAwWhcNMTkwNzA2MjM1OTU5WjA9MQswCQYDVQQGEwJGUjERMA8GA1UEChMIQ2Vy +dHBsdXMxGzAZBgNVBAMTEkNsYXNzIDIgUHJpbWFyeSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBANxQltAS+DXSCHh6tlJw/W/uz7kRy1134ezpfgSN1sxvc0NXYKwzCkTsA18cgCSR +5aiRVhKC9+Ar9NuuYS6JEI1rbLqzAr3VNsVINyPi8Fo3UjMXEuLRYE2+L0ER4/YXJQyLkcAbmXuZ +Vg2v7tK8R1fjeUl7NIknJITesezpWE7+Tt9avkGtrAjFGA7v0lPubNCdEgETjdyAYveVqUSISnFO +YFWe2yMZeVYHDD9jC1yw4r5+FfyUM1hBOHTE4Y+L3yasH7WLO7dDWWuwJKZtkIvEcupdM5i3y95e +e++U8Rs+yskhwcWYAqqi9lt3m/V+llU0HGdpwPFC40es/CgcZlUCAwEAAaOBjDCBiTAPBgNVHRME +CDAGAQH/AgEKMAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQU43Mt38sOKAze3bOkynm4jrvoMIkwEQYJ +YIZIAYb4QgEBBAQDAgEGMDcGA1UdHwQwMC4wLKAqoCiGJmh0dHA6Ly93d3cuY2VydHBsdXMuY29t +L0NSTC9jbGFzczIuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQCnVM+IRBnL39R/AN9WM2K191EBkOvD +P9GIROkkXe/nFL0gt5o8AP5tn9uQ3Nf0YtaLcF3n5QRIqWh8yfFC82x/xXp8HVGIutIKPidd3i1R +TtMTZGnkLuPT55sJmabglZvOGtd/vjzOUrMRFcEPF80Du5wlFbqidon8BvEY0JNLDnyCt6X09l/+ +7UCmnYR0ObncHoUW2ikbhiMAybuJfm6AiB4vFLQDJKgybwOaRywwvlbGp0ICcBvqQNi6BQNwB6SW +//1IMwrh3KWBkJtN3X3n57LNXMhqlfil9o3EXXgIvnsG1knPGTZQIy4I5p4FTUcY1Rbpsda2ENW7 +l7+ijrRU +-----END CERTIFICATE----- + +DST Root CA X3 +============== +-----BEGIN CERTIFICATE----- +MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/MSQwIgYDVQQK +ExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMTDkRTVCBSb290IENBIFgzMB4X +DTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVowPzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1 +cmUgVHJ1c3QgQ28uMRcwFQYDVQQDEw5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmT +rE4Orz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEqOLl5CjH9 +UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9bxiqKqy69cK3FCxolkHRy +xXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40d +utolucbY38EVAjqr2m7xPi71XAicPNaDaeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0T +AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQ +MA0GCSqGSIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69ikug +dB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXrAvHRAosZy5Q6XkjE +GB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZzR8srzJmwN0jP41ZL9c8PDHIyh8bw +RLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubS +fZGL+T0yjWW06XyxV3bqxbYoOb8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ +-----END CERTIFICATE----- + +DST ACES CA X6 +============== +-----BEGIN CERTIFICATE----- +MIIECTCCAvGgAwIBAgIQDV6ZCtadt3js2AdWO4YV2TANBgkqhkiG9w0BAQUFADBbMQswCQYDVQQG +EwJVUzEgMB4GA1UEChMXRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QxETAPBgNVBAsTCERTVCBBQ0VT +MRcwFQYDVQQDEw5EU1QgQUNFUyBDQSBYNjAeFw0wMzExMjAyMTE5NThaFw0xNzExMjAyMTE5NTha +MFsxCzAJBgNVBAYTAlVTMSAwHgYDVQQKExdEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdDERMA8GA1UE +CxMIRFNUIEFDRVMxFzAVBgNVBAMTDkRTVCBBQ0VTIENBIFg2MIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEAuT31LMmU3HWKlV1j6IR3dma5WZFcRt2SPp/5DgO0PWGSvSMmtWPuktKe1jzI +DZBfZIGxqAgNTNj50wUoUrQBJcWVHAx+PhCEdc/BGZFjz+iokYi5Q1K7gLFViYsx+tC3dr5BPTCa +pCIlF3PoHuLTrCq9Wzgh1SpL11V94zpVvddtawJXa+ZHfAjIgrrep4c9oW24MFbCswKBXy314pow +GCi4ZtPLAZZv6opFVdbgnf9nKxcCpk4aahELfrd755jWjHZvwTvbUJN+5dCOHze4vbrGn2zpfDPy +MjwmR/onJALJfh1biEITajV8fTXpLmaRcpPVMibEdPVTo7NdmvYJywIDAQABo4HIMIHFMA8GA1Ud +EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgHGMB8GA1UdEQQYMBaBFHBraS1vcHNAdHJ1c3Rkc3Qu +Y29tMGIGA1UdIARbMFkwVwYKYIZIAWUDAgEBATBJMEcGCCsGAQUFBwIBFjtodHRwOi8vd3d3LnRy +dXN0ZHN0LmNvbS9jZXJ0aWZpY2F0ZXMvcG9saWN5L0FDRVMtaW5kZXguaHRtbDAdBgNVHQ4EFgQU +CXIGThhDD+XWzMNqizF7eI+og7gwDQYJKoZIhvcNAQEFBQADggEBAKPYjtay284F5zLNAdMEA+V2 +5FYrnJmQ6AgwbN99Pe7lv7UkQIRJ4dEorsTCOlMwiPH1d25Ryvr/ma8kXxug/fKshMrfqfBfBC6t +Fr8hlxCBPeP/h40y3JTlR4peahPJlJU90u7INJXQgNStMgiAVDzgvVJT11J8smk/f3rPanTK+gQq +nExaBqXpIK1FZg9p8d2/6eMyi/rgwYZNcjwu2JN4Cir42NInPRmJX1p7ijvMDNpRrscL9yuwNwXs +vFcj4jjSm2jzVhKIT0J8uDHEtdvkyCE06UgRNe76x5JXxZ805Mf29w4LTJxoeHtxMcfrHuBnQfO3 +oKfN5XozNmr6mis= +-----END CERTIFICATE----- + +TURKTRUST Certificate Services Provider Root 1 +============================================== +-----BEGIN CERTIFICATE----- +MIID+zCCAuOgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBtzE/MD0GA1UEAww2VMOcUktUUlVTVCBF +bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGDAJUUjEP +MA0GA1UEBwwGQU5LQVJBMVYwVAYDVQQKDE0oYykgMjAwNSBUw5xSS1RSVVNUIEJpbGdpIMSwbGV0 +acWfaW0gdmUgQmlsacWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLjAeFw0wNTA1MTMx +MDI3MTdaFw0xNTAzMjIxMDI3MTdaMIG3MT8wPQYDVQQDDDZUw5xSS1RSVVNUIEVsZWt0cm9uaWsg +U2VydGlmaWthIEhpem1ldCBTYcSfbGF5xLFjxLFzxLExCzAJBgNVBAYMAlRSMQ8wDQYDVQQHDAZB +TktBUkExVjBUBgNVBAoMTShjKSAyMDA1IFTDnFJLVFJVU1QgQmlsZ2kgxLBsZXRpxZ9pbSB2ZSBC +aWxpxZ9pbSBHw7x2ZW5sacSfaSBIaXptZXRsZXJpIEEuxZ4uMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEAylIF1mMD2Bxf3dJ7XfIMYGFbazt0K3gNfUW9InTojAPBxhEqPZW8qZSwu5GX +yGl8hMW0kWxsE2qkVa2kheiVfrMArwDCBRj1cJ02i67L5BuBf5OI+2pVu32Fks66WJ/bMsW9Xe8i +Si9BB35JYbOG7E6mQW6EvAPs9TscyB/C7qju6hJKjRTP8wrgUDn5CDX4EVmt5yLqS8oUBt5CurKZ +8y1UiBAG6uEaPj1nH/vO+3yC6BFdSsG5FOpU2WabfIl9BJpiyelSPJ6c79L1JuTm5Rh8i27fbMx4 +W09ysstcP4wFjdFMjK2Sx+F4f2VsSQZQLJ4ywtdKxnWKWU51b0dewQIDAQABoxAwDjAMBgNVHRME +BTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAV9VX/N5aAWSGk/KEVTCD21F/aAyT8z5Aa9CEKmu46 +sWrv7/hg0Uw2ZkUd82YCdAR7kjCo3gp2D++Vbr3JN+YaDayJSFvMgzbC9UZcWYJWtNX+I7TYVBxE +q8Sn5RTOPEFhfEPmzcSBCYsk+1Ql1haolgxnB2+zUEfjHCQo3SqYpGH+2+oSN7wBGjSFvW5P55Fy +B0SFHljKVETd96y5y4khctuPwGkplyqjrhgjlxxBKot8KsF8kOipKMDTkcatKIdAaLX/7KfS0zgY +nNN9aV3wxqUeJBujR/xpB2jn5Jq07Q+hh4cCzofSSE7hvP/L8XKSRGQDJereW26fyfJOrN3H +-----END CERTIFICATE----- + +TURKTRUST Certificate Services Provider Root 2 +============================================== +-----BEGIN CERTIFICATE----- +MIIEPDCCAySgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBvjE/MD0GA1UEAww2VMOcUktUUlVTVCBF +bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGEwJUUjEP +MA0GA1UEBwwGQW5rYXJhMV0wWwYDVQQKDFRUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUg +QmlsacWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLiAoYykgS2FzxLFtIDIwMDUwHhcN +MDUxMTA3MTAwNzU3WhcNMTUwOTE2MTAwNzU3WjCBvjE/MD0GA1UEAww2VMOcUktUUlVTVCBFbGVr +dHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGEwJUUjEPMA0G +A1UEBwwGQW5rYXJhMV0wWwYDVQQKDFRUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmls +acWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLiAoYykgS2FzxLFtIDIwMDUwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCpNn7DkUNMwxmYCMjHWHtPFoylzkkBH3MOrHUTpvqe +LCDe2JAOCtFp0if7qnefJ1Il4std2NiDUBd9irWCPwSOtNXwSadktx4uXyCcUHVPr+G1QRT0mJKI +x+XlZEdhR3n9wFHxwZnn3M5q+6+1ATDcRhzviuyV79z/rxAc653YsKpqhRgNF8k+v/Gb0AmJQv2g +QrSdiVFVKc8bcLyEVK3BEx+Y9C52YItdP5qtygy/p1Zbj3e41Z55SZI/4PGXJHpsmxcPbe9TmJEr +5A++WXkHeLuXlfSfadRYhwqp48y2WBmfJiGxxFmNskF1wK1pzpwACPI2/z7woQ8arBT9pmAPAgMB +AAGjQzBBMB0GA1UdDgQWBBTZN7NOBf3Zz58SFq62iS/rJTqIHDAPBgNVHQ8BAf8EBQMDBwYAMA8G +A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAHJglrfJ3NgpXiOFX7KzLXb7iNcX/ntt +Rbj2hWyfIvwqECLsqrkw9qtY1jkQMZkpAL2JZkH7dN6RwRgLn7Vhy506vvWolKMiVW4XSf/SKfE4 +Jl3vpao6+XF75tpYHdN0wgH6PmlYX63LaL4ULptswLbcoCb6dxriJNoaN+BnrdFzgw2lGh1uEpJ+ +hGIAF728JRhX8tepb1mIvDS3LoV4nZbcFMMsilKbloxSZj2GFotHuFEJjOp9zYhys2AzsfAKRO8P +9Qk3iCQOLGsgOqL6EfJANZxEaGM7rDNvY7wsu/LSy3Z9fYjYHcgFHW68lKlmjHdxx/qR+i9Rnuk5 +UrbnBEI= +-----END CERTIFICATE----- + +SwissSign Gold CA - G2 +====================== +-----BEGIN CERTIFICATE----- +MIIFujCCA6KgAwIBAgIJALtAHEP1Xk+wMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNVBAYTAkNIMRUw +EwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMTFlN3aXNzU2lnbiBHb2xkIENBIC0gRzIwHhcN +MDYxMDI1MDgzMDM1WhcNMzYxMDI1MDgzMDM1WjBFMQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dp +c3NTaWduIEFHMR8wHQYDVQQDExZTd2lzc1NpZ24gR29sZCBDQSAtIEcyMIICIjANBgkqhkiG9w0B +AQEFAAOCAg8AMIICCgKCAgEAr+TufoskDhJuqVAtFkQ7kpJcyrhdhJJCEyq8ZVeCQD5XJM1QiyUq +t2/876LQwB8CJEoTlo8jE+YoWACjR8cGp4QjK7u9lit/VcyLwVcfDmJlD909Vopz2q5+bbqBHH5C +jCA12UNNhPqE21Is8w4ndwtrvxEvcnifLtg+5hg3Wipy+dpikJKVyh+c6bM8K8vzARO/Ws/BtQpg +vd21mWRTuKCWs2/iJneRjOBiEAKfNA+k1ZIzUd6+jbqEemA8atufK+ze3gE/bk3lUIbLtK/tREDF +ylqM2tIrfKjuvqblCqoOpd8FUrdVxyJdMmqXl2MT28nbeTZ7hTpKxVKJ+STnnXepgv9VHKVxaSvR +AiTysybUa9oEVeXBCsdtMDeQKuSeFDNeFhdVxVu1yzSJkvGdJo+hB9TGsnhQ2wwMC3wLjEHXuend +jIj3o02yMszYF9rNt85mndT9Xv+9lz4pded+p2JYryU0pUHHPbwNUMoDAw8IWh+Vc3hiv69yFGkO +peUDDniOJihC8AcLYiAQZzlG+qkDzAQ4embvIIO1jEpWjpEA/I5cgt6IoMPiaG59je883WX0XaxR +7ySArqpWl2/5rX3aYT+YdzylkbYcjCbaZaIJbcHiVOO5ykxMgI93e2CaHt+28kgeDrpOVG2Y4OGi +GqJ3UM/EY5LsRxmd6+ZrzsECAwEAAaOBrDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUWyV7lqRlUX64OfPAeGZe6Drn8O4wHwYDVR0jBBgwFoAUWyV7lqRlUX64 +OfPAeGZe6Drn8O4wRgYDVR0gBD8wPTA7BglghXQBWQECAQEwLjAsBggrBgEFBQcCARYgaHR0cDov +L3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBACe645R88a7A3hfm +5djV9VSwg/S7zV4Fe0+fdWavPOhWfvxyeDgD2StiGwC5+OlgzczOUYrHUDFu4Up+GC9pWbY9ZIEr +44OE5iKHjn3g7gKZYbge9LgriBIWhMIxkziWMaa5O1M/wySTVltpkuzFwbs4AOPsF6m43Md8AYOf +Mke6UiI0HTJ6CVanfCU2qT1L2sCCbwq7EsiHSycR+R4tx5M/nttfJmtS2S6K8RTGRI0Vqbe/vd6m +Gu6uLftIdxf+u+yvGPUqUfA5hJeVbG4bwyvEdGB5JbAKJ9/fXtI5z0V9QkvfsywexcZdylU6oJxp +mo/a77KwPJ+HbBIrZXAVUjEaJM9vMSNQH4xPjyPDdEFjHFWoFN0+4FFQz/EbMFYOkrCChdiDyyJk +vC24JdVUorgG6q2SpCSgwYa1ShNqR88uC1aVVMvOmttqtKay20EIhid392qgQmwLOM7XdVAyksLf +KzAiSNDVQTglXaTpXZ/GlHXQRf0wl0OPkKsKx4ZzYEppLd6leNcG2mqeSz53OiATIgHQv2ieY2Br +NU0LbbqhPcCT4H8js1WtciVORvnSFu+wZMEBnunKoGqYDs/YYPIvSbjkQuE4NRb0yG5P94FW6Lqj +viOvrv1vA+ACOzB2+httQc8Bsem4yWb02ybzOqR08kkkW8mw0FfB+j564ZfJ +-----END CERTIFICATE----- + +SwissSign Silver CA - G2 +======================== +-----BEGIN CERTIFICATE----- +MIIFvTCCA6WgAwIBAgIITxvUL1S7L0swDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCQ0gxFTAT +BgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMB4X +DTA2MTAyNTA4MzI0NloXDTM2MTAyNTA4MzI0NlowRzELMAkGA1UEBhMCQ0gxFTATBgNVBAoTDFN3 +aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMIICIjANBgkqhkiG +9w0BAQEFAAOCAg8AMIICCgKCAgEAxPGHf9N4Mfc4yfjDmUO8x/e8N+dOcbpLj6VzHVxumK4DV644 +N0MvFz0fyM5oEMF4rhkDKxD6LHmD9ui5aLlV8gREpzn5/ASLHvGiTSf5YXu6t+WiE7brYT7QbNHm ++/pe7R20nqA1W6GSy/BJkv6FCgU+5tkL4k+73JU3/JHpMjUi0R86TieFnbAVlDLaYQ1HTWBCrpJH +6INaUFjpiou5XaHc3ZlKHzZnu0jkg7Y360g6rw9njxcH6ATK72oxh9TAtvmUcXtnZLi2kUpCe2Uu +MGoM9ZDulebyzYLs2aFK7PayS+VFheZteJMELpyCbTapxDFkH4aDCyr0NQp4yVXPQbBH6TCfmb5h +qAaEuSh6XzjZG6k4sIN/c8HDO0gqgg8hm7jMqDXDhBuDsz6+pJVpATqJAHgE2cn0mRmrVn5bi4Y5 +FZGkECwJMoBgs5PAKrYYC51+jUnyEEp/+dVGLxmSo5mnJqy7jDzmDrxHB9xzUfFwZC8I+bRHHTBs +ROopN4WSaGa8gzj+ezku01DwH/teYLappvonQfGbGHLy9YR0SslnxFSuSGTfjNFusB3hB48IHpmc +celM2KX3RxIfdNFRnobzwqIjQAtz20um53MGjMGg6cFZrEb65i/4z3GcRm25xBWNOHkDRUjvxF3X +CO6HOSKGsg0PWEP3calILv3q1h8CAwEAAaOBrDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQUF6DNweRBtjpbO8tFnb0cwpj6hlgwHwYDVR0jBBgwFoAUF6DNweRB +tjpbO8tFnb0cwpj6hlgwRgYDVR0gBD8wPTA7BglghXQBWQEDAQEwLjAsBggrBgEFBQcCARYgaHR0 +cDovL3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBAHPGgeAn0i0P +4JUw4ppBf1AsX19iYamGamkYDHRJ1l2E6kFSGG9YrVBWIGrGvShpWJHckRE1qTodvBqlYJ7YH39F +kWnZfrt4csEGDyrOj4VwYaygzQu4OSlWhDJOhrs9xCrZ1x9y7v5RoSJBsXECYxqCsGKrXlcSH9/L +3XWgwF15kIwb4FDm3jH+mHtwX6WQ2K34ArZv02DdQEsixT2tOnqfGhpHkXkzuoLcMmkDlm4fS/Bx +/uNncqCxv1yL5PqZIseEuRuNI5c/7SXgz2W79WEE790eslpBIlqhn10s6FvJbakMDHiqYMZWjwFa +DGi8aRl5xB9+lwW/xekkUV7U1UtT7dkjWjYDZaPBA61BMPNGG4WQr2W11bHkFlt4dR2Xem1ZqSqP +e97Dh4kQmUlzeMg9vVE1dCrV8X5pGyq7O70luJpaPXJhkGaH7gzWTdQRdAtq/gsD/KNVV4n+Ssuu +WxcFyPKNIzFTONItaj+CuY0IavdeQXRuwxF+B6wpYJE/OMpXEA29MC/HpeZBoNquBYeaoKRlbEwJ +DIm6uNO5wJOKMPqN5ZprFQFOZ6raYlY+hAhm0sQ2fac+EPyI4NSA5QC9qvNOBqN6avlicuMJT+ub +DgEj8Z+7fNzcbBGXJbLytGMU0gYqZ4yD9c7qB9iaah7s5Aq7KkzrCWA5zspi2C5u +-----END CERTIFICATE----- + +GeoTrust Primary Certification Authority +======================================== +-----BEGIN CERTIFICATE----- +MIIDfDCCAmSgAwIBAgIQGKy1av1pthU6Y2yv2vrEoTANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQG +EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UEAxMoR2VvVHJ1c3QgUHJpbWFyeSBD +ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjExMjcwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMFgx +CzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTEwLwYDVQQDEyhHZW9UcnVzdCBQ +cmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEAvrgVe//UfH1nrYNke8hCUy3f9oQIIGHWAVlqnEQRr+92/ZV+zmEwu3qDXwK9AWbK7hWN +b6EwnL2hhZ6UOvNWiAAxz9juapYC2e0DjPt1befquFUWBRaa9OBesYjAZIVcFU2Ix7e64HXprQU9 +nceJSOC7KMgD4TCTZF5SwFlwIjVXiIrxlQqD17wxcwE07e9GceBrAqg1cmuXm2bgyxx5X9gaBGge +RwLmnWDiNpcB3841kt++Z8dtd1k7j53WkBWUvEI0EME5+bEnPn7WinXFsq+W06Lem+SYvn3h6YGt +tm/81w7a4DSwDRp35+MImO9Y+pyEtzavwt+s0vQQBnBxNQIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQULNVQQZcVi/CPNmFbSvtr2ZnJM5IwDQYJKoZI +hvcNAQEFBQADggEBAFpwfyzdtzRP9YZRqSa+S7iq8XEN3GHHoOo0Hnp3DwQ16CePbJC/kRYkRj5K +Ts4rFtULUh38H2eiAkUxT87z+gOneZ1TatnaYzr4gNfTmeGl4b7UVXGYNTq+k+qurUKykG/g/CFN +NWMziUnWm07Kx+dOCQD32sfvmWKZd7aVIl6KoKv0uHiYyjgZmclynnjNS6yvGaBzEi38wkG6gZHa +Floxt/m0cYASSJlyc1pZU8FjUjPtp8nSOQJw+uCxQmYpqptR7TBUIhRf2asdweSU8Pj1K/fqynhG +1riR/aYNKxoUAT6A8EKglQdebc3MS6RFjasS6LPeWuWgfOgPIh1a6Vk= +-----END CERTIFICATE----- + +thawte Primary Root CA +====================== +-----BEGIN CERTIFICATE----- +MIIEIDCCAwigAwIBAgIQNE7VVyDV7exJ9C/ON9srbTANBgkqhkiG9w0BAQUFADCBqTELMAkGA1UE +BhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2 +aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhv +cml6ZWQgdXNlIG9ubHkxHzAdBgNVBAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMDYxMTE3 +MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCBqTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwg +SW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMv +KGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNVBAMT +FnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCs +oPD7gFnUnMekz52hWXMJEEUMDSxuaPFsW0hoSVk3/AszGcJ3f8wQLZU0HObrTQmnHNK4yZc2AreJ +1CRfBsDMRJSUjQJib+ta3RGNKJpchJAQeg29dGYvajig4tVUROsdB58Hum/u6f1OCyn1PoSgAfGc +q/gcfomk6KHYcWUNo1F77rzSImANuVud37r8UVsLr5iy6S7pBOhih94ryNdOwUxkHt3Ph1i6Sk/K +aAcdHJ1KxtUvkcx8cXIcxcBn6zL9yZJclNqFwJu/U30rCfSMnZEfl2pSy94JNqR32HuHUETVPm4p +afs5SSYeCaWAe0At6+gnhcn+Yf1+5nyXHdWdAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYD +VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBR7W0XPr87Lev0xkhpqtvNG61dIUDANBgkqhkiG9w0BAQUF +AAOCAQEAeRHAS7ORtvzw6WfUDW5FvlXok9LOAz/t2iWwHVfLHjp2oEzsUHboZHIMpKnxuIvW1oeE +uzLlQRHAd9mzYJ3rG9XRbkREqaYB7FViHXe4XI5ISXycO1cRrK1zN44veFyQaEfZYGDm/Ac9IiAX +xPcW6cTYcvnIc3zfFi8VqT79aie2oetaupgf1eNNZAqdE8hhuvU5HIe6uL17In/2/qxAeeWsEG89 +jxt5dovEN7MhGITlNgDrYyCZuen+MwS7QcjBAvlEYyCegc5C09Y/LHbTY5xZ3Y+m4Q6gLkH3LpVH +z7z9M/P2C2F+fpErgUfCJzDupxBdN49cOSvkBPB7jVaMaA== +-----END CERTIFICATE----- + +VeriSign Class 3 Public Primary Certification Authority - G5 +============================================================ +-----BEGIN CERTIFICATE----- +MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCByjELMAkGA1UE +BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBO +ZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVk +IHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCB +yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2ln +biBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZvciBh +dXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmlt +YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQCvJAgIKXo1nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKz +j/i5Vbext0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIzSdhD +Y2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQGBO+QueQA5N06tRn/ +Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+rCpSx4/VBEnkjWNHiDxpg8v+R70r +fk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/ +BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2Uv +Z2lmMCEwHzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy +aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKvMzEzMA0GCSqG +SIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzEp6B4Eq1iDkVwZMXnl2YtmAl+ +X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKE +KQsTb47bDN0lAtukixlE0kF6BWlKWE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiC +Km0oHw0LxOXnGiYZ4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vE +ZV8NhnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq +-----END CERTIFICATE----- + +SecureTrust CA +============== +-----BEGIN CERTIFICATE----- +MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBIMQswCQYDVQQG +EwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xFzAVBgNVBAMTDlNlY3VyZVRy +dXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIzMTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAe +BgNVBAoTF1NlY3VyZVRydXN0IENvcnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCC +ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQX +OZEzZum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO0gMdA+9t +DWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIaowW8xQmxSPmjL8xk037uH +GFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b +01k/unK8RCSc43Oz969XL0Imnal0ugBS8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmH +ursCAwEAAaOBnTCBmjATBgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCegJYYj +aHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQAwDQYJ +KoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt36Z3q059c4EVlew3KW+JwULKUBRSu +SceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHf +mbx8IVQr5Fiiu1cprp6poxkmD5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZ +nMUFdAvnZyPSCPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR +3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE= +-----END CERTIFICATE----- + +Secure Global CA +================ +-----BEGIN CERTIFICATE----- +MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBKMQswCQYDVQQG +EwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBH +bG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkxMjMxMTk1MjA2WjBKMQswCQYDVQQGEwJVUzEg +MB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwg +Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxVaQZx5RNoJLNP2MwhR/jx +YDiJiQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6MpjhHZevj8fcyTiW89sa/FHtaMbQ +bqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ/kG5VacJjnIFHovdRIWCQtBJwB1g +8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnIHmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYV +HDGA76oYa8J719rO+TMg1fW9ajMtgQT7sFzUnKPiXB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi +0XPnj3pDAgMBAAGjgZ0wgZowEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud +EwEB/wQFMAMBAf8wHQYDVR0OBBYEFK9EBMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCswKaAn +oCWGI2h0dHA6Ly9jcmwuc2VjdXJldHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsGAQQBgjcVAQQDAgEA +MA0GCSqGSIb3DQEBBQUAA4IBAQBjGghAfaReUw132HquHw0LURYD7xh8yOOvaliTFGCRsoTciE6+ +OYo68+aCiV0BN7OrJKQVDpI1WkpEXk5X+nXOH0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cn +CDpOGR86p1hcF895P4vkp9MmI50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/5 +3CYNv6ZHdAbYiNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc +f8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW +-----END CERTIFICATE----- + +COMODO Certification Authority +============================== +-----BEGIN CERTIFICATE----- +MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCBgTELMAkGA1UE +BhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgG +A1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNVBAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1 +dGhvcml0eTAeFw0wNjEyMDEwMDAwMDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEb +MBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFD +T01PRE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3UcEbVASY06m/weaKXTuH ++7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI2GqGd0S7WWaXUF601CxwRM/aN5VCaTww +xHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV +4EajcNxo2f8ESIl33rXp+2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA +1KGzqSX+DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5OnKVI +rLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW/zAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6gPKA6hjhodHRwOi8vY3JsLmNvbW9k +b2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOC +AQEAPpiem/Yb6dc5t3iuHXIYSdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CP +OGEIqB6BCsAvIC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/ +RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4zJVSk/BwJVmc +IGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5ddBA6+C4OmF4O5MBKgxTMVBbkN ++8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IBZQ== +-----END CERTIFICATE----- + +Network Solutions Certificate Authority +======================================= +-----BEGIN CERTIFICATE----- +MIID5jCCAs6gAwIBAgIQV8szb8JcFuZHFhfjkDFo4DANBgkqhkiG9w0BAQUFADBiMQswCQYDVQQG +EwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMuMTAwLgYDVQQDEydOZXR3b3Jr +IFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDYxMjAxMDAwMDAwWhcNMjkxMjMx +MjM1OTU5WjBiMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMu +MTAwLgYDVQQDEydOZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkvH6SMG3G2I4rC7xGzuAnlt7e+foS0zwzc7MEL7xx +jOWftiJgPl9dzgn/ggwbmlFQGiaJ3dVhXRncEg8tCqJDXRfQNJIg6nPPOCwGJgl6cvf6UDL4wpPT +aaIjzkGxzOTVHzbRijr4jGPiFFlp7Q3Tf2vouAPlT2rlmGNpSAW+Lv8ztumXWWn4Zxmuk2GWRBXT +crA/vGp97Eh/jcOrqnErU2lBUzS1sLnFBgrEsEX1QV1uiUV7PTsmjHTC5dLRfbIR1PtYMiKagMnc +/Qzpf14Dl847ABSHJ3A4qY5usyd2mFHgBeMhqxrVhSI8KbWaFsWAqPS7azCPL0YCorEMIuDTAgMB +AAGjgZcwgZQwHQYDVR0OBBYEFCEwyfsA106Y2oeqKtCnLrFAMadMMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MFIGA1UdHwRLMEkwR6BFoEOGQWh0dHA6Ly9jcmwubmV0c29sc3NsLmNv +bS9OZXR3b3JrU29sdXRpb25zQ2VydGlmaWNhdGVBdXRob3JpdHkuY3JsMA0GCSqGSIb3DQEBBQUA +A4IBAQC7rkvnt1frf6ott3NHhWrB5KUd5Oc86fRZZXe1eltajSU24HqXLjjAV2CDmAaDn7l2em5Q +4LqILPxFzBiwmZVRDuwduIj/h1AcgsLj4DKAv6ALR8jDMe+ZZzKATxcheQxpXN5eNK4CtSbqUN9/ +GGUsyfJj4akH/nxxH2szJGoeBfcFaMBqEssuXmHLrijTfsK0ZpEmXzwuJF/LWA/rKOyvEZbz3Htv +wKeI8lN3s2Berq4o2jUsbzRF0ybh3uxbTydrFny9RAQYgrOJeRcQcT16ohZO9QHNpGxlaKFJdlxD +ydi8NmdspZS11My5vWo1ViHe2MPr+8ukYEywVaCge1ey +-----END CERTIFICATE----- + +WellsSecure Public Root Certificate Authority +============================================= +-----BEGIN CERTIFICATE----- +MIIEvTCCA6WgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoM +F1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYw +NAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcN +MDcxMjEzMTcwNzU0WhcNMjIxMjE0MDAwNzU0WjCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoMF1dl +bGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYD +VQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDub7S9eeKPCCGeOARBJe+rWxxTkqxtnt3CxC5FlAM1 +iGd0V+PfjLindo8796jE2yljDpFoNoqXjopxaAkH5OjUDk/41itMpBb570OYj7OeUt9tkTmPOL13 +i0Nj67eT/DBMHAGTthP796EfvyXhdDcsHqRePGj4S78NuR4uNuip5Kf4D8uCdXw1LSLWwr8L87T8 +bJVhHlfXBIEyg1J55oNjz7fLY4sR4r1e6/aN7ZVyKLSsEmLpSjPmgzKuBXWVvYSV2ypcm44uDLiB +K0HmOFafSZtsdvqKXfcBeYF8wYNABf5x/Qw/zE5gCQ5lRxAvAcAFP4/4s0HvWkJ+We/SlwxlAgMB +AAGjggE0MIIBMDAPBgNVHRMBAf8EBTADAQH/MDkGA1UdHwQyMDAwLqAsoCqGKGh0dHA6Ly9jcmwu +cGtpLndlbGxzZmFyZ28uY29tL3dzcHJjYS5jcmwwDgYDVR0PAQH/BAQDAgHGMB0GA1UdDgQWBBQm +lRkQ2eihl5H/3BnZtQQ+0nMKajCBsgYDVR0jBIGqMIGngBQmlRkQ2eihl5H/3BnZtQQ+0nMKaqGB +i6SBiDCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoMF1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRww +GgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMg +Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHmCAQEwDQYJKoZIhvcNAQEFBQADggEBALkVsUSRzCPI +K0134/iaeycNzXK7mQDKfGYZUMbVmO2rvwNa5U3lHshPcZeG1eMd/ZDJPHV3V3p9+N701NX3leZ0 +bh08rnyd2wIDBSxxSyU+B+NemvVmFymIGjifz6pBA4SXa5M4esowRBskRDPQ5NHcKDj0E0M1NSlj +qHyita04pO2t/caaH/+Xc/77szWnk4bGdpEA5qxRFsQnMlzbc9qlk1eOPm01JghZ1edE13YgY+es +E2fDbbFwRnzVlhE9iW9dqKHrjQrawx0zbKPqZxmamX9LPYNRKh3KL4YMon4QLSvUFpULB6ouFJJJ +tylv2G0xffX8oRAHh84vWdw+WNs= +-----END CERTIFICATE----- + +COMODO ECC Certification Authority +================================== +-----BEGIN CERTIFICATE----- +MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTELMAkGA1UEBhMC +R0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UE +ChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwHhcNMDgwMzA2MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0Ix +GzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR +Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRo +b3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSRFtSrYpn1PlILBs5BAH+X +4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0JcfRK9ChQtP6IHG4/bC8vCVlbpVsLM5ni +wz2J+Wos77LTBumjQjBAMB0GA1UdDgQWBBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VG +FAkK+qDmfQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdvGDeA +U/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY= +-----END CERTIFICATE----- + +IGC/A +===== +-----BEGIN CERTIFICATE----- +MIIEAjCCAuqgAwIBAgIFORFFEJQwDQYJKoZIhvcNAQEFBQAwgYUxCzAJBgNVBAYTAkZSMQ8wDQYD +VQQIEwZGcmFuY2UxDjAMBgNVBAcTBVBhcmlzMRAwDgYDVQQKEwdQTS9TR0ROMQ4wDAYDVQQLEwVE +Q1NTSTEOMAwGA1UEAxMFSUdDL0ExIzAhBgkqhkiG9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2LmZy +MB4XDTAyMTIxMzE0MjkyM1oXDTIwMTAxNzE0MjkyMlowgYUxCzAJBgNVBAYTAkZSMQ8wDQYDVQQI +EwZGcmFuY2UxDjAMBgNVBAcTBVBhcmlzMRAwDgYDVQQKEwdQTS9TR0ROMQ4wDAYDVQQLEwVEQ1NT +STEOMAwGA1UEAxMFSUdDL0ExIzAhBgkqhkiG9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2LmZyMIIB +IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsh/R0GLFMzvABIaIs9z4iPf930Pfeo2aSVz2 +TqrMHLmh6yeJ8kbpO0px1R2OLc/mratjUMdUC24SyZA2xtgv2pGqaMVy/hcKshd+ebUyiHDKcMCW +So7kVc0dJ5S/znIq7Fz5cyD+vfcuiWe4u0dzEvfRNWk68gq5rv9GQkaiv6GFGvm/5P9JhfejcIYy +HF2fYPepraX/z9E0+X1bF8bc1g4oa8Ld8fUzaJ1O/Id8NhLWo4DoQw1VYZTqZDdH6nfK0LJYBcNd +frGoRpAxVs5wKpayMLh35nnAvSk7/ZR3TL0gzUEl4C7HG7vupARB0l2tEmqKm0f7yd1GQOGdPDPQ +tQIDAQABo3cwdTAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBRjAVBgNVHSAEDjAMMAoGCCqB +egF5AQEBMB0GA1UdDgQWBBSjBS8YYFDCiQrdKyFP/45OqDAxNjAfBgNVHSMEGDAWgBSjBS8YYFDC +iQrdKyFP/45OqDAxNjANBgkqhkiG9w0BAQUFAAOCAQEABdwm2Pp3FURo/C9mOnTgXeQp/wYHE4RK +q89toB9RlPhJy3Q2FLwV3duJL92PoF189RLrn544pEfMs5bZvpwlqwN+Mw+VgQ39FuCIvjfwbF3Q +MZsyK10XZZOYYLxuj7GoPB7ZHPOpJkL5ZB3C55L29B5aqhlSXa/oovdgoPaN8In1buAKBQGVyYsg +Crpa/JosPL3Dt8ldeCUFP1YUmwza+zpI/pdpXsoQhvdOlgQITeywvl3cO45Pwf2aNjSaTFR+FwNI +lQgRHAdvhQh+XU3Endv7rs6y0bO4g2wdsrN58dhwmX7wEwLOXt1R0982gaEbeC9xs/FZTEYYKKuF +0mBWWg== +-----END CERTIFICATE----- + +Security Communication EV RootCA1 +================================= +-----BEGIN CERTIFICATE----- +MIIDfTCCAmWgAwIBAgIBADANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJKUDElMCMGA1UEChMc +U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEqMCgGA1UECxMhU2VjdXJpdHkgQ29tbXVuaWNh +dGlvbiBFViBSb290Q0ExMB4XDTA3MDYwNjAyMTIzMloXDTM3MDYwNjAyMTIzMlowYDELMAkGA1UE +BhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xKjAoBgNVBAsTIVNl +Y3VyaXR5IENvbW11bmljYXRpb24gRVYgUm9vdENBMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBALx/7FebJOD+nLpCeamIivqA4PUHKUPqjgo0No0c+qe1OXj/l3X3L+SqawSERMqm4miO +/VVQYg+kcQ7OBzgtQoVQrTyWb4vVog7P3kmJPdZkLjjlHmy1V4qe70gOzXppFodEtZDkBp2uoQSX +WHnvIEqCa4wiv+wfD+mEce3xDuS4GBPMVjZd0ZoeUWs5bmB2iDQL87PRsJ3KYeJkHcFGB7hj3R4z +ZbOOCVVSPbW9/wfrrWFVGCypaZhKqkDFMxRldAD5kd6vA0jFQFTcD4SQaCDFkpbcLuUCRarAX1T4 +bepJz11sS6/vmsJWXMY1VkJqMF/Cq/biPT+zyRGPMUzXn0kCAwEAAaNCMEAwHQYDVR0OBBYEFDVK +9U2vP9eCOKyrcWUXdYydVZPmMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqG +SIb3DQEBBQUAA4IBAQCoh+ns+EBnXcPBZsdAS5f8hxOQWsTvoMpfi7ent/HWtWS3irO4G8za+6xm +iEHO6Pzk2x6Ipu0nUBsCMCRGef4Eh3CXQHPRwMFXGZpppSeZq51ihPZRwSzJIxXYKLerJRO1RuGG +Av8mjMSIkh1W/hln8lXkgKNrnKt34VFxDSDbEJrbvXZ5B3eZKK2aXtqxT0QsNY6llsf9g/BYxnnW +mHyojf6GPgcWkuF75x3sM3Z+Qi5KhfmRiWiEA4Glm5q+4zfFVKtWOxgtQaQM+ELbmaDgcm+7XeEW +T1MKZPlO9L9OVL14bIjqv5wTJMJwaaJ/D8g8rQjJsJhAoyrniIPtd490 +-----END CERTIFICATE----- + +OISTE WISeKey Global Root GA CA +=============================== +-----BEGIN CERTIFICATE----- +MIID8TCCAtmgAwIBAgIQQT1yx/RrH4FDffHSKFTfmjANBgkqhkiG9w0BAQUFADCBijELMAkGA1UE +BhMCQ0gxEDAOBgNVBAoTB1dJU2VLZXkxGzAZBgNVBAsTEkNvcHlyaWdodCAoYykgMjAwNTEiMCAG +A1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBH +bG9iYWwgUm9vdCBHQSBDQTAeFw0wNTEyMTExNjAzNDRaFw0zNzEyMTExNjA5NTFaMIGKMQswCQYD +VQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEbMBkGA1UECxMSQ29weXJpZ2h0IChjKSAyMDA1MSIw +IAYDVQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5 +IEdsb2JhbCBSb290IEdBIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy0+zAJs9 +Nt350UlqaxBJH+zYK7LG+DKBKUOVTJoZIyEVRd7jyBxRVVuuk+g3/ytr6dTqvirdqFEr12bDYVxg +Asj1znJ7O7jyTmUIms2kahnBAbtzptf2w93NvKSLtZlhuAGio9RN1AU9ka34tAhxZK9w8RxrfvbD +d50kc3vkDIzh2TbhmYsFmQvtRTEJysIA2/dyoJaqlYfQjse2YXMNdmaM3Bu0Y6Kff5MTMPGhJ9vZ +/yxViJGg4E8HsChWjBgbl0SOid3gF27nKu+POQoxhILYQBRJLnpB5Kf+42TMwVlxSywhp1t94B3R +LoGbw9ho972WG6xwsRYUC9tguSYBBQIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUswN+rja8sHnR3JQmthG+IbJphpQwEAYJKwYBBAGCNxUBBAMCAQAwDQYJ +KoZIhvcNAQEFBQADggEBAEuh/wuHbrP5wUOxSPMowB0uyQlB+pQAHKSkq0lPjz0e701vvbyk9vIm +MMkQyh2I+3QZH4VFvbBsUfk2ftv1TDI6QU9bR8/oCy22xBmddMVHxjtqD6wU2zz0c5ypBd8A3HR4 ++vg1YFkCExh8vPtNsCBtQ7tgMHpnM1zFmdH4LTlSc/uMqpclXHLZCB6rTjzjgTGfA6b7wP4piFXa +hNVQA7bihKOmNqoROgHhGEvWRGizPflTdISzRpFGlgC3gCy24eMQ4tui5yiPAZZiFj4A4xylNoEY +okxSdsARo27mHbrjWr42U8U+dY+GaSlYU7Wcu2+fXMUY7N0v4ZjJ/L7fCg0= +-----END CERTIFICATE----- + +Microsec e-Szigno Root CA +========================= +-----BEGIN CERTIFICATE----- +MIIHqDCCBpCgAwIBAgIRAMy4579OKRr9otxmpRwsDxEwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UE +BhMCSFUxETAPBgNVBAcTCEJ1ZGFwZXN0MRYwFAYDVQQKEw1NaWNyb3NlYyBMdGQuMRQwEgYDVQQL +EwtlLVN6aWdubyBDQTEiMCAGA1UEAxMZTWljcm9zZWMgZS1Temlnbm8gUm9vdCBDQTAeFw0wNTA0 +MDYxMjI4NDRaFw0xNzA0MDYxMjI4NDRaMHIxCzAJBgNVBAYTAkhVMREwDwYDVQQHEwhCdWRhcGVz +dDEWMBQGA1UEChMNTWljcm9zZWMgTHRkLjEUMBIGA1UECxMLZS1Temlnbm8gQ0ExIjAgBgNVBAMT +GU1pY3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQDtyADVgXvNOABHzNuEwSFpLHSQDCHZU4ftPkNEU6+r+ICbPHiN1I2uuO/TEdyB5s87lozWbxXG +d36hL+BfkrYn13aaHUM86tnsL+4582pnS4uCzyL4ZVX+LMsvfUh6PXX5qqAnu3jCBspRwn5mS6/N +oqdNAoI/gqyFxuEPkEeZlApxcpMqyabAvjxWTHOSJ/FrtfX9/DAFYJLG65Z+AZHCabEeHXtTRbjc +QR/Ji3HWVBTji1R4P770Yjtb9aPs1ZJ04nQw7wHb4dSrmZsqa/i9phyGI0Jf7Enemotb9HI6QMVJ +PqW+jqpx62z69Rrkav17fVVA71hu5tnVvCSrwe+3AgMBAAGjggQ3MIIEMzBnBggrBgEFBQcBAQRb +MFkwKAYIKwYBBQUHMAGGHGh0dHBzOi8vcmNhLmUtc3ppZ25vLmh1L29jc3AwLQYIKwYBBQUHMAKG +IWh0dHA6Ly93d3cuZS1zemlnbm8uaHUvUm9vdENBLmNydDAPBgNVHRMBAf8EBTADAQH/MIIBcwYD +VR0gBIIBajCCAWYwggFiBgwrBgEEAYGoGAIBAQEwggFQMCgGCCsGAQUFBwIBFhxodHRwOi8vd3d3 +LmUtc3ppZ25vLmh1L1NaU1ovMIIBIgYIKwYBBQUHAgIwggEUHoIBEABBACAAdABhAG4A+gBzAO0A +dAB2AOEAbgB5ACAA6QByAHQAZQBsAG0AZQB6AOkAcwDpAGgAZQB6ACAA6QBzACAAZQBsAGYAbwBn +AGEAZADhAHMA4QBoAG8AegAgAGEAIABTAHoAbwBsAGcA4QBsAHQAYQB0APMAIABTAHoAbwBsAGcA +4QBsAHQAYQB0AOEAcwBpACAAUwB6AGEAYgDhAGwAeQB6AGEAdABhACAAcwB6AGUAcgBpAG4AdAAg +AGsAZQBsAGwAIABlAGwAagDhAHIAbgBpADoAIABoAHQAdABwADoALwAvAHcAdwB3AC4AZQAtAHMA +egBpAGcAbgBvAC4AaAB1AC8AUwBaAFMAWgAvMIHIBgNVHR8EgcAwgb0wgbqggbeggbSGIWh0dHA6 +Ly93d3cuZS1zemlnbm8uaHUvUm9vdENBLmNybIaBjmxkYXA6Ly9sZGFwLmUtc3ppZ25vLmh1L0NO +PU1pY3Jvc2VjJTIwZS1Temlnbm8lMjBSb290JTIwQ0EsT1U9ZS1Temlnbm8lMjBDQSxPPU1pY3Jv +c2VjJTIwTHRkLixMPUJ1ZGFwZXN0LEM9SFU/Y2VydGlmaWNhdGVSZXZvY2F0aW9uTGlzdDtiaW5h +cnkwDgYDVR0PAQH/BAQDAgEGMIGWBgNVHREEgY4wgYuBEGluZm9AZS1zemlnbm8uaHWkdzB1MSMw +IQYDVQQDDBpNaWNyb3NlYyBlLVN6aWduw7MgUm9vdCBDQTEWMBQGA1UECwwNZS1TemlnbsOzIEhT +WjEWMBQGA1UEChMNTWljcm9zZWMgS2Z0LjERMA8GA1UEBxMIQnVkYXBlc3QxCzAJBgNVBAYTAkhV +MIGsBgNVHSMEgaQwgaGAFMegSXUWYYTbMUuE0vE3QJDvTtz3oXakdDByMQswCQYDVQQGEwJIVTER +MA8GA1UEBxMIQnVkYXBlc3QxFjAUBgNVBAoTDU1pY3Jvc2VjIEx0ZC4xFDASBgNVBAsTC2UtU3pp +Z25vIENBMSIwIAYDVQQDExlNaWNyb3NlYyBlLVN6aWdubyBSb290IENBghEAzLjnv04pGv2i3Gal +HCwPETAdBgNVHQ4EFgQUx6BJdRZhhNsxS4TS8TdAkO9O3PcwDQYJKoZIhvcNAQEFBQADggEBANMT +nGZjWS7KXHAM/IO8VbH0jgdsZifOwTsgqRy7RlRw7lrMoHfqaEQn6/Ip3Xep1fvj1KcExJW4C+FE +aGAHQzAxQmHl7tnlJNUb3+FKG6qfx1/4ehHqE5MAyopYse7tDk2016g2JnzgOsHVV4Lxdbb9iV/a +86g4nzUGCM4ilb7N1fy+W955a9x6qWVmvrElWl/tftOsRm1M9DKHtCAE4Gx4sHfRhUZLphK3dehK +yVZs15KrnfVJONJPU+NVkBHbmJbGSfI+9J8b4PeI3CVimUTYc78/MPMMNz7UwiiAc7EBt51alhQB +S6kRnSlqLtBdgcDPsiBDxwPgN05dCtxZICU= +-----END CERTIFICATE----- + +Certigna +======== +-----BEGIN CERTIFICATE----- +MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNVBAYTAkZSMRIw +EAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4XDTA3MDYyOTE1MTMwNVoXDTI3 +MDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczERMA8GA1UEAwwI +Q2VydGlnbmEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIaPHJ1tazNHUmgh7stL7q +XOEm7RFHYeGifBZ4QCHkYJ5ayGPhxLGWkv8YbWkj4Sti993iNi+RB7lIzw7sebYs5zRLcAglozyH +GxnygQcPOJAZ0xH+hrTy0V4eHpbNgGzOOzGTtvKg0KmVEn2lmsxryIRWijOp5yIVUxbwzBfsV1/p +ogqYCd7jX5xv3EjjhQsVWqa6n6xI4wmy9/Qy3l40vhx4XUJbzg4ij02Q130yGLMLLGq/jj8UEYkg +DncUtT2UCIf3JR7VsmAA7G8qKCVuKj4YYxclPz5EIBb2JsglrgVKtOdjLPOMFlN+XPsRGgjBRmKf +Irjxwo1p3Po6WAbfAgMBAAGjgbwwgbkwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUGu3+QTmQ +tCRZvgHyUtVF9lo53BEwZAYDVR0jBF0wW4AUGu3+QTmQtCRZvgHyUtVF9lo53BGhOKQ2MDQxCzAJ +BgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hggkA/tzjAQ/J +SP8wDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzANBgkqhkiG9w0BAQUFAAOCAQEA +hQMeknH2Qq/ho2Ge6/PAD/Kl1NqV5ta+aDY9fm4fTIrv0Q8hbV6lUmPOEvjvKtpv6zf+EwLHyzs+ +ImvaYS5/1HI93TDhHkxAGYwP15zRgzB7mFncfca5DClMoTOi62c6ZYTTluLtdkVwj7Ur3vkj1klu +PBS1xp81HlDQwY9qcEQCYsuuHWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY +1gkIl2PlwS6wt0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw +WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg== +-----END CERTIFICATE----- + +AC Ra\xC3\xADz Certic\xC3\xA1mara S.A. +====================================== +-----BEGIN CERTIFICATE----- +MIIGZjCCBE6gAwIBAgIPB35Sk3vgFeNX8GmMy+wMMA0GCSqGSIb3DQEBBQUAMHsxCzAJBgNVBAYT +AkNPMUcwRQYDVQQKDD5Tb2NpZWRhZCBDYW1lcmFsIGRlIENlcnRpZmljYWNpw7NuIERpZ2l0YWwg +LSBDZXJ0aWPDoW1hcmEgUy5BLjEjMCEGA1UEAwwaQUMgUmHDrXogQ2VydGljw6FtYXJhIFMuQS4w +HhcNMDYxMTI3MjA0NjI5WhcNMzAwNDAyMjE0MjAyWjB7MQswCQYDVQQGEwJDTzFHMEUGA1UECgw+ +U29jaWVkYWQgQ2FtZXJhbCBkZSBDZXJ0aWZpY2FjacOzbiBEaWdpdGFsIC0gQ2VydGljw6FtYXJh +IFMuQS4xIzAhBgNVBAMMGkFDIFJhw616IENlcnRpY8OhbWFyYSBTLkEuMIICIjANBgkqhkiG9w0B +AQEFAAOCAg8AMIICCgKCAgEAq2uJo1PMSCMI+8PPUZYILrgIem08kBeGqentLhM0R7LQcNzJPNCN +yu5LF6vQhbCnIwTLqKL85XXbQMpiiY9QngE9JlsYhBzLfDe3fezTf3MZsGqy2IiKLUV0qPezuMDU +2s0iiXRNWhU5cxh0T7XrmafBHoi0wpOQY5fzp6cSsgkiBzPZkc0OnB8OIMfuuzONj8LSWKdf/WU3 +4ojC2I+GdV75LaeHM/J4Ny+LvB2GNzmxlPLYvEqcgxhaBvzz1NS6jBUJJfD5to0EfhcSM2tXSExP +2yYe68yQ54v5aHxwD6Mq0Do43zeX4lvegGHTgNiRg0JaTASJaBE8rF9ogEHMYELODVoqDA+bMMCm +8Ibbq0nXl21Ii/kDwFJnmxL3wvIumGVC2daa49AZMQyth9VXAnow6IYm+48jilSH5L887uvDdUhf +HjlvgWJsxS3EF1QZtzeNnDeRyPYL1epjb4OsOMLzP96a++EjYfDIJss2yKHzMI+ko6Kh3VOz3vCa +Mh+DkXkwwakfU5tTohVTP92dsxA7SH2JD/ztA/X7JWR1DhcZDY8AFmd5ekD8LVkH2ZD6mq093ICK +5lw1omdMEWux+IBkAC1vImHFrEsm5VoQgpukg3s0956JkSCXjrdCx2bD0Omk1vUgjcTDlaxECp1b +czwmPS9KvqfJpxAe+59QafMCAwEAAaOB5jCB4zAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE +AwIBBjAdBgNVHQ4EFgQU0QnQ6dfOeXRU+Tows/RtLAMDG2gwgaAGA1UdIASBmDCBlTCBkgYEVR0g +ADCBiTArBggrBgEFBQcCARYfaHR0cDovL3d3dy5jZXJ0aWNhbWFyYS5jb20vZHBjLzBaBggrBgEF +BQcCAjBOGkxMaW1pdGFjaW9uZXMgZGUgZ2FyYW507WFzIGRlIGVzdGUgY2VydGlmaWNhZG8gc2Ug +cHVlZGVuIGVuY29udHJhciBlbiBsYSBEUEMuMA0GCSqGSIb3DQEBBQUAA4ICAQBclLW4RZFNjmEf +AygPU3zmpFmps4p6xbD/CHwso3EcIRNnoZUSQDWDg4902zNc8El2CoFS3UnUmjIz75uny3XlesuX +EpBcunvFm9+7OSPI/5jOCk0iAUgHforA1SBClETvv3eiiWdIG0ADBaGJ7M9i4z0ldma/Jre7Ir5v +/zlXdLp6yQGVwZVR6Kss+LGGIOk/yzVb0hfpKv6DExdA7ohiZVvVO2Dpezy4ydV/NgIlqmjCMRW3 +MGXrfx1IebHPOeJCgBbT9ZMj/EyXyVo3bHwi2ErN0o42gzmRkBDI8ck1fj+404HGIGQatlDCIaR4 +3NAvO2STdPCWkPHv+wlaNECW8DYSwaN0jJN+Qd53i+yG2dIPPy3RzECiiWZIHiCznCNZc6lEc7wk +eZBWN7PGKX6jD/EpOe9+XCgycDWs2rjIdWb8m0w5R44bb5tNAlQiM+9hup4phO9OSzNHdpdqy35f +/RWmnkJDW2ZaiogN9xa5P1FlK2Zqi9E4UqLWRhH6/JocdJ6PlwsCT2TG9WjTSy3/pDceiz+/RL5h +RqGEPQgnTIEgd4kI6mdAXmwIUV80WoyWaM3X94nCHNMyAK9Sy9NgWyo6R35rMDOhYil/SrnhLecU +Iw4OGEfhefwVVdCx/CVxY3UzHCMrr1zZ7Ud3YA47Dx7SwNxkBYn8eNZcLCZDqQ== +-----END CERTIFICATE----- + +TC TrustCenter Class 2 CA II +============================ +-----BEGIN CERTIFICATE----- +MIIEqjCCA5KgAwIBAgIOLmoAAQACH9dSISwRXDswDQYJKoZIhvcNAQEFBQAwdjELMAkGA1UEBhMC +REUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxIjAgBgNVBAsTGVRDIFRydXN0Q2VudGVy +IENsYXNzIDIgQ0ExJTAjBgNVBAMTHFRDIFRydXN0Q2VudGVyIENsYXNzIDIgQ0EgSUkwHhcNMDYw +MTEyMTQzODQzWhcNMjUxMjMxMjI1OTU5WjB2MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1 +c3RDZW50ZXIgR21iSDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMiBDQTElMCMGA1UE +AxMcVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMiBDQSBJSTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBAKuAh5uO8MN8h9foJIIRszzdQ2Lu+MNF2ujhoF/RKrLqk2jftMjWQ+nEdVl//OEd+DFw +IxuInie5e/060smp6RQvkL4DUsFJzfb95AhmC1eKokKguNV/aVyQMrKXDcpK3EY+AlWJU+MaWss2 +xgdW94zPEfRMuzBwBJWl9jmM/XOBCH2JXjIeIqkiRUuwZi4wzJ9l/fzLganx4Duvo4bRierERXlQ +Xa7pIXSSTYtZgo+U4+lK8edJsBTj9WLL1XK9H7nSn6DNqPoByNkN39r8R52zyFTfSUrxIan+GE7u +SNQZu+995OKdy1u2bv/jzVrndIIFuoAlOMvkaZ6vQaoahPUCAwEAAaOCATQwggEwMA8GA1UdEwEB +/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTjq1RMgKHbVkO3kUrL84J6E1wIqzCB +7QYDVR0fBIHlMIHiMIHfoIHcoIHZhjVodHRwOi8vd3d3LnRydXN0Y2VudGVyLmRlL2NybC92Mi90 +Y19jbGFzc18yX2NhX0lJLmNybIaBn2xkYXA6Ly93d3cudHJ1c3RjZW50ZXIuZGUvQ049VEMlMjBU +cnVzdENlbnRlciUyMENsYXNzJTIwMiUyMENBJTIwSUksTz1UQyUyMFRydXN0Q2VudGVyJTIwR21i +SCxPVT1yb290Y2VydHMsREM9dHJ1c3RjZW50ZXIsREM9ZGU/Y2VydGlmaWNhdGVSZXZvY2F0aW9u +TGlzdD9iYXNlPzANBgkqhkiG9w0BAQUFAAOCAQEAjNfffu4bgBCzg/XbEeprS6iSGNn3Bzn1LL4G +dXpoUxUc6krtXvwjshOg0wn/9vYua0Fxec3ibf2uWWuFHbhOIprtZjluS5TmVfwLG4t3wVMTZonZ +KNaL80VKY7f9ewthXbhtvsPcW3nS7Yblok2+XnR8au0WOB9/WIFaGusyiC2y8zl3gK9etmF1Kdsj +TYjKUCjLhdLTEKJZbtOTVAB6okaVhgWcqRmY5TFyDADiZ9lA4CQze28suVyrZZ0srHbqNZn1l7kP +JOzHdiEoZa5X6AeIdUpWoNIFOqTmjZKILPPy4cHGYdtBxceb9w4aUUXCYWvcZCcXjFq32nQozZfk +vQ== +-----END CERTIFICATE----- + +TC TrustCenter Class 3 CA II +============================ +-----BEGIN CERTIFICATE----- +MIIEqjCCA5KgAwIBAgIOSkcAAQAC5aBd1j8AUb8wDQYJKoZIhvcNAQEFBQAwdjELMAkGA1UEBhMC +REUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxIjAgBgNVBAsTGVRDIFRydXN0Q2VudGVy +IENsYXNzIDMgQ0ExJTAjBgNVBAMTHFRDIFRydXN0Q2VudGVyIENsYXNzIDMgQ0EgSUkwHhcNMDYw +MTEyMTQ0MTU3WhcNMjUxMjMxMjI1OTU5WjB2MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1 +c3RDZW50ZXIgR21iSDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMyBDQTElMCMGA1UE +AxMcVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMyBDQSBJSTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBALTgu1G7OVyLBMVMeRwjhjEQY0NVJz/GRcekPewJDRoeIMJWHt4bNwcwIi9v8Qbxq63W +yKthoy9DxLCyLfzDlml7forkzMA5EpBCYMnMNWju2l+QVl/NHE1bWEnrDgFPZPosPIlY2C8u4rBo +6SI7dYnWRBpl8huXJh0obazovVkdKyT21oQDZogkAHhg8fir/gKya/si+zXmFtGt9i4S5Po1auUZ +uV3bOx4a+9P/FRQI2AlqukWdFHlgfa9Aigdzs5OW03Q0jTo3Kd5c7PXuLjHCINy+8U9/I1LZW+Jk +2ZyqBwi1Rb3R0DHBq1SfqdLDYmAD8bs5SpJKPQq5ncWg/jcCAwEAAaOCATQwggEwMA8GA1UdEwEB +/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTUovyfs8PYA9NXXAek0CSnwPIA1DCB +7QYDVR0fBIHlMIHiMIHfoIHcoIHZhjVodHRwOi8vd3d3LnRydXN0Y2VudGVyLmRlL2NybC92Mi90 +Y19jbGFzc18zX2NhX0lJLmNybIaBn2xkYXA6Ly93d3cudHJ1c3RjZW50ZXIuZGUvQ049VEMlMjBU +cnVzdENlbnRlciUyMENsYXNzJTIwMyUyMENBJTIwSUksTz1UQyUyMFRydXN0Q2VudGVyJTIwR21i +SCxPVT1yb290Y2VydHMsREM9dHJ1c3RjZW50ZXIsREM9ZGU/Y2VydGlmaWNhdGVSZXZvY2F0aW9u +TGlzdD9iYXNlPzANBgkqhkiG9w0BAQUFAAOCAQEANmDkcPcGIEPZIxpC8vijsrlNirTzwppVMXzE +O2eatN9NDoqTSheLG43KieHPOh6sHfGcMrSOWXaiQYUlN6AT0PV8TtXqluJucsG7Kv5sbviRmEb8 +yRtXW+rIGjs/sFGYPAfaLFkB2otE6OF0/ado3VS6g0bsyEa1+K+XwDsJHI/OcpY9M1ZwvJbL2NV9 +IJqDnxrcOfHFcqMRA/07QlIp2+gB95tejNaNhk4Z+rwcvsUhpYeeeC422wlxo3I0+GzjBgnyXlal +092Y+tTmBvTwtiBjS+opvaqCZh77gaqnN60TGOaSw4HBM7uIHqHn4rS9MWwOUT1v+5ZWgOI2F9Hc +5A== +-----END CERTIFICATE----- + +TC TrustCenter Universal CA I +============================= +-----BEGIN CERTIFICATE----- +MIID3TCCAsWgAwIBAgIOHaIAAQAC7LdggHiNtgYwDQYJKoZIhvcNAQEFBQAweTELMAkGA1UEBhMC +REUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxJDAiBgNVBAsTG1RDIFRydXN0Q2VudGVy +IFVuaXZlcnNhbCBDQTEmMCQGA1UEAxMdVEMgVHJ1c3RDZW50ZXIgVW5pdmVyc2FsIENBIEkwHhcN +MDYwMzIyMTU1NDI4WhcNMjUxMjMxMjI1OTU5WjB5MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMg +VHJ1c3RDZW50ZXIgR21iSDEkMCIGA1UECxMbVEMgVHJ1c3RDZW50ZXIgVW5pdmVyc2FsIENBMSYw +JAYDVQQDEx1UQyBUcnVzdENlbnRlciBVbml2ZXJzYWwgQ0EgSTCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAKR3I5ZEr5D0MacQ9CaHnPM42Q9e3s9B6DGtxnSRJJZ4Hgmgm5qVSkr1YnwC +qMqs+1oEdjneX/H5s7/zA1hV0qq34wQi0fiU2iIIAI3TfCZdzHd55yx4Oagmcw6iXSVphU9VDprv +xrlE4Vc93x9UIuVvZaozhDrzznq+VZeujRIPFDPiUHDDSYcTvFHe15gSWu86gzOSBnWLknwSaHtw +ag+1m7Z3W0hZneTvWq3zwZ7U10VOylY0Ibw+F1tvdwxIAUMpsN0/lm7mlaoMwCC2/T42J5zjXM9O +gdwZu5GQfezmlwQek8wiSdeXhrYTCjxDI3d+8NzmzSQfO4ObNDqDNOMCAwEAAaNjMGEwHwYDVR0j +BBgwFoAUkqR1LKSevoFE63n8isWVpesQdXMwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AYYwHQYDVR0OBBYEFJKkdSyknr6BROt5/IrFlaXrEHVzMA0GCSqGSIb3DQEBBQUAA4IBAQAo0uCG +1eb4e/CX3CJrO5UUVg8RMKWaTzqwOuAGy2X17caXJ/4l8lfmXpWMPmRgFVp/Lw0BxbFg/UU1z/Cy +vwbZ71q+s2IhtNerNXxTPqYn8aEt2hojnczd7Dwtnic0XQ/CNnm8yUpiLe1r2X1BQ3y2qsrtYbE3 +ghUJGooWMNjsydZHcnhLEEYUjl8Or+zHL6sQ17bxbuyGssLoDZJz3KL0Dzq/YSMQiZxIQG5wALPT +ujdEWBF6AmqI8Dc08BnprNRlc/ZpjGSUOnmFKbAWKwyCPwacx/0QK54PLLae4xW/2TYcuiUaUj0a +7CIMHOCkoj3w6DnPgcB77V0fb8XQC9eY +-----END CERTIFICATE----- + +Deutsche Telekom Root CA 2 +========================== +-----BEGIN CERTIFICATE----- +MIIDnzCCAoegAwIBAgIBJjANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJERTEcMBoGA1UEChMT +RGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxlU2VjIFRydXN0IENlbnRlcjEjMCEG +A1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290IENBIDIwHhcNOTkwNzA5MTIxMTAwWhcNMTkwNzA5 +MjM1OTAwWjBxMQswCQYDVQQGEwJERTEcMBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0G +A1UECxMWVC1UZWxlU2VjIFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBS +b290IENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrC6M14IspFLEUha88EOQ5 +bzVdSq7d6mGNlUn0b2SjGmBmpKlAIoTZ1KXleJMOaAGtuU1cOs7TuKhCQN/Po7qCWWqSG6wcmtoI +KyUn+WkjR/Hg6yx6m/UTAtB+NHzCnjwAWav12gz1MjwrrFDa1sPeg5TKqAyZMg4ISFZbavva4VhY +AUlfckE8FQYBjl2tqriTtM2e66foai1SNNs671x1Udrb8zH57nGYMsRUFUQM+ZtV7a3fGAigo4aK +Se5TBY8ZTNXeWHmb0mocQqvF1afPaA+W5OFhmHZhyJF81j4A4pFQh+GdCuatl9Idxjp9y7zaAzTV +jlsB9WoHtxa2bkp/AgMBAAGjQjBAMB0GA1UdDgQWBBQxw3kbuvVT1xfgiXotF2wKsyudMzAPBgNV +HRMECDAGAQH/AgEFMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAlGRZrTlk5ynr +E/5aw4sTV8gEJPB0d8Bg42f76Ymmg7+Wgnxu1MM9756AbrsptJh6sTtU6zkXR34ajgv8HzFZMQSy +zhfzLMdiNlXiItiJVbSYSKpk+tYcNthEeFpaIzpXl/V6ME+un2pMSyuOoAPjPuCp1NJ70rOo4nI8 +rZ7/gFnkm0W09juwzTkZmDLl6iFhkOQxIY40sfcvNUqFENrnijchvllj4PKFiDFT1FQUhXB59C4G +dyd1Lx+4ivn+xbrYNuSD7Odlt79jWvNGr4GUN9RBjNYj1h7P9WgbRGOiWrqnNVmh5XAFmw4jV5mU +Cm26OWMohpLzGITY+9HPBVZkVw== +-----END CERTIFICATE----- + +ComSign Secured CA +================== +-----BEGIN CERTIFICATE----- +MIIDqzCCApOgAwIBAgIRAMcoRwmzuGxFjB36JPU2TukwDQYJKoZIhvcNAQEFBQAwPDEbMBkGA1UE +AxMSQ29tU2lnbiBTZWN1cmVkIENBMRAwDgYDVQQKEwdDb21TaWduMQswCQYDVQQGEwJJTDAeFw0w +NDAzMjQxMTM3MjBaFw0yOTAzMTYxNTA0NTZaMDwxGzAZBgNVBAMTEkNvbVNpZ24gU2VjdXJlZCBD +QTEQMA4GA1UEChMHQ29tU2lnbjELMAkGA1UEBhMCSUwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQDGtWhfHZQVw6QIVS3joFd67+l0Kru5fFdJGhFeTymHDEjWaueP1H5XJLkGieQcPOqs +49ohgHMhCu95mGwfCP+hUH3ymBvJVG8+pSjsIQQPRbsHPaHA+iqYHU4Gk/v1iDurX8sWv+bznkqH +7Rnqwp9D5PGBpX8QTz7RSmKtUxvLg/8HZaWSLWapW7ha9B20IZFKF3ueMv5WJDmyVIRD9YTC2LxB +kMyd1mja6YJQqTtoz7VdApRgFrFD2UNd3V2Hbuq7s8lr9gOUCXDeFhF6K+h2j0kQmHe5Y1yLM5d1 +9guMsqtb3nQgJT/j8xH5h2iGNXHDHYwt6+UarA9z1YJZQIDTAgMBAAGjgacwgaQwDAYDVR0TBAUw +AwEB/zBEBgNVHR8EPTA7MDmgN6A1hjNodHRwOi8vZmVkaXIuY29tc2lnbi5jby5pbC9jcmwvQ29t +U2lnblNlY3VyZWRDQS5jcmwwDgYDVR0PAQH/BAQDAgGGMB8GA1UdIwQYMBaAFMFL7XC29z58ADsA +j8c+DkWfHl3sMB0GA1UdDgQWBBTBS+1wtvc+fAA7AI/HPg5Fnx5d7DANBgkqhkiG9w0BAQUFAAOC +AQEAFs/ukhNQq3sUnjO2QiBq1BW9Cav8cujvR3qQrFHBZE7piL1DRYHjZiM/EoZNGeQFsOY3wo3a +BijJD4mkU6l1P7CW+6tMM1X5eCZGbxs2mPtCdsGCuY7e+0X5YxtiOzkGynd6qDwJz2w2PQ8KRUtp +FhpFfTMDZflScZAmlaxMDPWLkz/MdXSFmLr/YnpNH4n+rr2UAJm/EaXc4HnFFgt9AmEd6oX5AhVP +51qJThRv4zdLhfXBPGHg/QVBspJ/wx2g0K5SZGBrGMYmnNj1ZOQ2GmKfig8+/21OGVZOIJFsnzQz +OjRXUDpvgV4GxvU+fE6OK85lBi5d0ipTdF7Tbieejw== +-----END CERTIFICATE----- + +Cybertrust Global Root +====================== +-----BEGIN CERTIFICATE----- +MIIDoTCCAomgAwIBAgILBAAAAAABD4WqLUgwDQYJKoZIhvcNAQEFBQAwOzEYMBYGA1UEChMPQ3li +ZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2JhbCBSb290MB4XDTA2MTIxNTA4 +MDAwMFoXDTIxMTIxNTA4MDAwMFowOzEYMBYGA1UEChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQD +ExZDeWJlcnRydXN0IEdsb2JhbCBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA ++Mi8vRRQZhP/8NN57CPytxrHjoXxEnOmGaoQ25yiZXRadz5RfVb23CO21O1fWLE3TdVJDm71aofW +0ozSJ8bi/zafmGWgE07GKmSb1ZASzxQG9Dvj1Ci+6A74q05IlG2OlTEQXO2iLb3VOm2yHLtgwEZL +AfVJrn5GitB0jaEMAs7u/OePuGtm839EAL9mJRQr3RAwHQeWP032a7iPt3sMpTjr3kfb1V05/Iin +89cqdPHoWqI7n1C6poxFNcJQZZXcY4Lv3b93TZxiyWNzFtApD0mpSPCzqrdsxacwOUBdrsTiXSZT +8M4cIwhhqJQZugRiQOwfOHB3EgZxpzAYXSUnpQIDAQABo4GlMIGiMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBS2CHsNesysIEyGVjJez6tuhS1wVzA/BgNVHR8EODA2 +MDSgMqAwhi5odHRwOi8vd3d3Mi5wdWJsaWMtdHJ1c3QuY29tL2NybC9jdC9jdHJvb3QuY3JsMB8G +A1UdIwQYMBaAFLYIew16zKwgTIZWMl7Pq26FLXBXMA0GCSqGSIb3DQEBBQUAA4IBAQBW7wojoFRO +lZfJ+InaRcHUowAl9B8Tq7ejhVhpwjCt2BWKLePJzYFa+HMjWqd8BfP9IjsO0QbE2zZMcwSO5bAi +5MXzLqXZI+O4Tkogp24CJJ8iYGd7ix1yCcUxXOl5n4BHPa2hCwcUPUf/A2kaDAtE52Mlp3+yybh2 +hO0j9n0Hq0V+09+zv+mKts2oomcrUtW3ZfA5TGOgkXmTUg9U3YO7n9GPp1Nzw8v/MOx8BLjYRB+T +X3EJIrduPuocA06dGiBh+4E37F78CkWr1+cXVdCg6mCbpvbjjFspwgZgFJ0tl0ypkxWdYcQBX0jW +WL1WMRJOEcgh4LMRkWXbtKaIOM5V +-----END CERTIFICATE----- + +ePKI Root Certification Authority +================================= +-----BEGIN CERTIFICATE----- +MIIFsDCCA5igAwIBAgIQFci9ZUdcr7iXAF7kBtK8nTANBgkqhkiG9w0BAQUFADBeMQswCQYDVQQG +EwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0ZC4xKjAoBgNVBAsMIWVQS0kg +Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNDEyMjAwMjMxMjdaFw0zNDEyMjAwMjMx +MjdaMF4xCzAJBgNVBAYTAlRXMSMwIQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEq +MCgGA1UECwwhZVBLSSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0B +AQEFAAOCAg8AMIICCgKCAgEA4SUP7o3biDN1Z82tH306Tm2d0y8U82N0ywEhajfqhFAHSyZbCUNs +IZ5qyNUD9WBpj8zwIuQf5/dqIjG3LBXy4P4AakP/h2XGtRrBp0xtInAhijHyl3SJCRImHJ7K2RKi +lTza6We/CKBk49ZCt0Xvl/T29de1ShUCWH2YWEtgvM3XDZoTM1PRYfl61dd4s5oz9wCGzh1NlDiv +qOx4UXCKXBCDUSH3ET00hl7lSM2XgYI1TBnsZfZrxQWh7kcT1rMhJ5QQCtkkO7q+RBNGMD+XPNjX +12ruOzjjK9SXDrkb5wdJfzcq+Xd4z1TtW0ado4AOkUPB1ltfFLqfpo0kR0BZv3I4sjZsN/+Z0V0O +WQqraffAsgRFelQArr5T9rXn4fg8ozHSqf4hUmTFpmfwdQcGlBSBVcYn5AGPF8Fqcde+S/uUWH1+ +ETOxQvdibBjWzwloPn9s9h6PYq2lY9sJpx8iQkEeb5mKPtf5P0B6ebClAZLSnT0IFaUQAS2zMnao +lQ2zepr7BxB4EW/hj8e6DyUadCrlHJhBmd8hh+iVBmoKs2pHdmX2Os+PYhcZewoozRrSgx4hxyy/ +vv9haLdnG7t4TY3OZ+XkwY63I2binZB1NJipNiuKmpS5nezMirH4JYlcWrYvjB9teSSnUmjDhDXi +Zo1jDiVN1Rmy5nk3pyKdVDECAwEAAaNqMGgwHQYDVR0OBBYEFB4M97Zn8uGSJglFwFU5Lnc/Qkqi +MAwGA1UdEwQFMAMBAf8wOQYEZyoHAAQxMC8wLQIBADAJBgUrDgMCGgUAMAcGBWcqAwAABBRFsMLH +ClZ87lt4DJX5GFPBphzYEDANBgkqhkiG9w0BAQUFAAOCAgEACbODU1kBPpVJufGBuvl2ICO1J2B0 +1GqZNF5sAFPZn/KmsSQHRGoqxqWOeBLoR9lYGxMqXnmbnwoqZ6YlPwZpVnPDimZI+ymBV3QGypzq +KOg4ZyYr8dW1P2WT+DZdjo2NQCCHGervJ8A9tDkPJXtoUHRVnAxZfVo9QZQlUgjgRywVMRnVvwdV +xrsStZf0X4OFunHB2WyBEXYKCrC/gpf36j36+uwtqSiUO1bd0lEursC9CBWMd1I0ltabrNMdjmEP +NXubrjlpC2JgQCA2j6/7Nu4tCEoduL+bXPjqpRugc6bY+G7gMwRfaKonh+3ZwZCc7b3jajWvY9+r +GNm65ulK6lCKD2GTHuItGeIwlDWSXQ62B68ZgI9HkFFLLk3dheLSClIKF5r8GrBQAuUBo2M3IUxE +xJtRmREOc5wGj1QupyheRDmHVi03vYVElOEMSyycw5KFNGHLD7ibSkNS/jQ6fbjpKdx2qcgw+BRx +gMYeNkh0IkFch4LoGHGLQYlE535YW6i4jRPpp2zDR+2zGp1iro2C6pSe3VkQw63d4k3jMdXH7Ojy +sP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTEW9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmOD +BCEIZ43ygknQW/2xzQ+DhNQ+IIX3Sj0rnP0qCglN6oH4EZw= +-----END CERTIFICATE----- + +T\xc3\x9c\x42\xC4\xB0TAK UEKAE K\xC3\xB6k Sertifika Hizmet Sa\xC4\x9Flay\xc4\xb1\x63\xc4\xb1s\xc4\xb1 - S\xC3\xBCr\xC3\xBCm 3 +============================================================================================================================= +-----BEGIN CERTIFICATE----- +MIIFFzCCA/+gAwIBAgIBETANBgkqhkiG9w0BAQUFADCCASsxCzAJBgNVBAYTAlRSMRgwFgYDVQQH +DA9HZWJ6ZSAtIEtvY2FlbGkxRzBFBgNVBAoMPlTDvHJraXllIEJpbGltc2VsIHZlIFRla25vbG9q +aWsgQXJhxZ90xLFybWEgS3VydW11IC0gVMOcQsSwVEFLMUgwRgYDVQQLDD9VbHVzYWwgRWxla3Ry +b25payB2ZSBLcmlwdG9sb2ppIEFyYcWfdMSxcm1hIEVuc3RpdMO8c8O8IC0gVUVLQUUxIzAhBgNV +BAsMGkthbXUgU2VydGlmaWthc3lvbiBNZXJrZXppMUowSAYDVQQDDEFUw5xCxLBUQUsgVUVLQUUg +S8O2ayBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsSAtIFPDvHLDvG0gMzAeFw0wNzA4 +MjQxMTM3MDdaFw0xNzA4MjExMTM3MDdaMIIBKzELMAkGA1UEBhMCVFIxGDAWBgNVBAcMD0dlYnpl +IC0gS29jYWVsaTFHMEUGA1UECgw+VMO8cmtpeWUgQmlsaW1zZWwgdmUgVGVrbm9sb2ppayBBcmHF +n3TEsXJtYSBLdXJ1bXUgLSBUw5xCxLBUQUsxSDBGBgNVBAsMP1VsdXNhbCBFbGVrdHJvbmlrIHZl +IEtyaXB0b2xvamkgQXJhxZ90xLFybWEgRW5zdGl0w7xzw7wgLSBVRUtBRTEjMCEGA1UECwwaS2Ft +dSBTZXJ0aWZpa2FzeW9uIE1lcmtlemkxSjBIBgNVBAMMQVTDnELEsFRBSyBVRUtBRSBLw7ZrIFNl +cnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxIC0gU8O8csO8bSAzMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEAim1L/xCIOsP2fpTo6iBkcK4hgb46ezzb8R1Sf1n68yJMlaCQvEhO +Eav7t7WNeoMojCZG2E6VQIdhn8WebYGHV2yKO7Rm6sxA/OOqbLLLAdsyv9Lrhc+hDVXDWzhXcLh1 +xnnRFDDtG1hba+818qEhTsXOfJlfbLm4IpNQp81McGq+agV/E5wrHur+R84EpW+sky58K5+eeROR +6Oqeyjh1jmKwlZMq5d/pXpduIF9fhHpEORlAHLpVK/swsoHvhOPc7Jg4OQOFCKlUAwUp8MmPi+oL +hmUZEdPpCSPeaJMDyTYcIW7OjGbxmTDY17PDHfiBLqi9ggtm/oLL4eAagsNAgQIDAQABo0IwQDAd +BgNVHQ4EFgQUvYiHyY/2pAoLquvF/pEjnatKijIwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF +MAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAB18+kmPNOm3JpIWmgV050vQbTlswyb2zrgxvMTfvCr4 +N5EY3ATIZJkrGG2AA1nJrvhY0D7twyOfaTyGOBye79oneNGEN3GKPEs5z35FBtYt2IpNeBLWrcLT +y9LQQfMmNkqblWwM7uXRQydmwYj3erMgbOqwaSvHIOgMA8RBBZniP+Rr+KCGgceExh/VS4ESshYh +LBOhgLJeDEoTniDYYkCrkOpkSi+sDQESeUWoL4cZaMjihccwsnX5OD+ywJO0a+IDRM5noN+J1q2M +dqMTw5RhK2vZbMEHCiIHhWyFJEapvj+LeISCfiQMnf2BN+MlqO02TpUsyZyQ2uypQjyttgI= +-----END CERTIFICATE----- + +Buypass Class 2 CA 1 +==================== +-----BEGIN CERTIFICATE----- +MIIDUzCCAjugAwIBAgIBATANBgkqhkiG9w0BAQUFADBLMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU +QnV5cGFzcyBBUy05ODMxNjMzMjcxHTAbBgNVBAMMFEJ1eXBhc3MgQ2xhc3MgMiBDQSAxMB4XDTA2 +MTAxMzEwMjUwOVoXDTE2MTAxMzEwMjUwOVowSzELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBh +c3MgQVMtOTgzMTYzMzI3MR0wGwYDVQQDDBRCdXlwYXNzIENsYXNzIDIgQ0EgMTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAIs8B0XY9t/mx8q6jUPFR42wWsE425KEHK8T1A9vNkYgxC7M +cXA0ojTTNy7Y3Tp3L8DrKehc0rWpkTSHIln+zNvnma+WwajHQN2lFYxuyHyXA8vmIPLXl18xoS83 +0r7uvqmtqEyeIWZDO6i88wmjONVZJMHCR3axiFyCO7srpgTXjAePzdVBHfCuuCkslFJgNJQ72uA4 +0Z0zPhX0kzLFANq1KWYOOngPIVJfAuWSeyXTkh4vFZ2B5J2O6O+JzhRMVB0cgRJNcKi+EAUXfh/R +uFdV7c27UsKwHnjCTTZoy1YmwVLBvXb3WNVyfh9EdrsAiR0WnVE1703CVu9r4Iw7DekCAwEAAaNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUP42aWYv8e3uco684sDntkHGA1sgwDgYDVR0P +AQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQAVGn4TirnoB6NLJzKyQJHyIdFkhb5jatLPgcIV +1Xp+DCmsNx4cfHZSldq1fyOhKXdlyTKdqC5Wq2B2zha0jX94wNWZUYN/Xtm+DKhQ7SLHrQVMdvvt +7h5HZPb3J31cKA9FxVxiXqaakZG3Uxcu3K1gnZZkOb1naLKuBctN518fV4bVIJwo+28TOPX2EZL2 +fZleHwzoq0QkKXJAPTZSr4xYkHPB7GEseaHsh7U/2k3ZIQAw3pDaDtMaSKk+hQsUi4y8QZ5q9w5w +wDX3OaJdZtB7WZ+oRxKaJyOkLY4ng5IgodcVf/EuGO70SH8vf/GhGLWhC5SgYiAynB321O+/TIho +-----END CERTIFICATE----- + +Buypass Class 3 CA 1 +==================== +-----BEGIN CERTIFICATE----- +MIIDUzCCAjugAwIBAgIBAjANBgkqhkiG9w0BAQUFADBLMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU +QnV5cGFzcyBBUy05ODMxNjMzMjcxHTAbBgNVBAMMFEJ1eXBhc3MgQ2xhc3MgMyBDQSAxMB4XDTA1 +MDUwOTE0MTMwM1oXDTE1MDUwOTE0MTMwM1owSzELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBh +c3MgQVMtOTgzMTYzMzI3MR0wGwYDVQQDDBRCdXlwYXNzIENsYXNzIDMgQ0EgMTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAKSO13TZKWTeXx+HgJHqTjnmGcZEC4DVC69TB4sSveZn8AKx +ifZgisRbsELRwCGoy+Gb72RRtqfPFfV0gGgEkKBYouZ0plNTVUhjP5JW3SROjvi6K//zNIqeKNc0 +n6wv1g/xpC+9UrJJhW05NfBEMJNGJPO251P7vGGvqaMU+8IXF4Rs4HyI+MkcVyzwPX6UvCWThOia +AJpFBUJXgPROztmuOfbIUxAMZTpHe2DC1vqRycZxbL2RhzyRhkmr8w+gbCZ2Xhysm3HljbybIR6c +1jh+JIAVMYKWsUnTYjdbiAwKYjT+p0h+mbEwi5A3lRyoH6UsjfRVyNvdWQrCrXig9IsCAwEAAaNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUOBTmyPCppAP0Tj4io1vy1uCtQHQwDgYDVR0P +AQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQABZ6OMySU9E2NdFm/soT4JXJEVKirZgCFPBdy7 +pYmrEzMqnji3jG8CcmPHc3ceCQa6Oyh7pEfJYWsICCD8igWKH7y6xsL+z27sEzNxZy5p+qksP2bA +EllNC1QCkoS72xLvg3BweMhT+t/Gxv/ciC8HwEmdMldg0/L2mSlf56oBzKwzqBwKu5HEA6BvtjT5 +htOzdlSY9EqBs1OdTUDs5XcTRa9bqh/YL0yCe/4qxFi7T/ye/QNlGioOw6UgFpRreaaiErS7GqQj +el/wroQk5PMr+4okoyeYZdowdXb8GZHo2+ubPzK/QJcHJrrM85SFSnonk8+QQtS4Wxam58tAA915 +-----END CERTIFICATE----- + +EBG Elektronik Sertifika Hizmet Sa\xC4\x9Flay\xc4\xb1\x63\xc4\xb1s\xc4\xb1 +========================================================================== +-----BEGIN CERTIFICATE----- +MIIF5zCCA8+gAwIBAgIITK9zQhyOdAIwDQYJKoZIhvcNAQEFBQAwgYAxODA2BgNVBAMML0VCRyBF +bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMTcwNQYDVQQKDC5FQkcg +QmlsacWfaW0gVGVrbm9sb2ppbGVyaSB2ZSBIaXptZXRsZXJpIEEuxZ4uMQswCQYDVQQGEwJUUjAe +Fw0wNjA4MTcwMDIxMDlaFw0xNjA4MTQwMDMxMDlaMIGAMTgwNgYDVQQDDC9FQkcgRWxla3Ryb25p +ayBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsTE3MDUGA1UECgwuRUJHIEJpbGnFn2lt +IFRla25vbG9qaWxlcmkgdmUgSGl6bWV0bGVyaSBBLsWeLjELMAkGA1UEBhMCVFIwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQDuoIRh0DpqZhAy2DE4f6en5f2h4fuXd7hxlugTlkaDT7by +X3JWbhNgpQGR4lvFzVcfd2NR/y8927k/qqk153nQ9dAktiHq6yOU/im/+4mRDGSaBUorzAzu8T2b +gmmkTPiab+ci2hC6X5L8GCcKqKpE+i4stPtGmggDg3KriORqcsnlZR9uKg+ds+g75AxuetpX/dfr +eYteIAbTdgtsApWjluTLdlHRKJ2hGvxEok3MenaoDT2/F08iiFD9rrbskFBKW5+VQarKD7JK/oCZ +TqNGFav4c0JqwmZ2sQomFd2TkuzbqV9UIlKRcF0T6kjsbgNs2d1s/OsNA/+mgxKb8amTD8UmTDGy +Y5lhcucqZJnSuOl14nypqZoaqsNW2xCaPINStnuWt6yHd6i58mcLlEOzrz5z+kI2sSXFCjEmN1Zn +uqMLfdb3ic1nobc6HmZP9qBVFCVMLDMNpkGMvQQxahByCp0OLna9XvNRiYuoP1Vzv9s6xiQFlpJI +qkuNKgPlV5EQ9GooFW5Hd4RcUXSfGenmHmMWOeMRFeNYGkS9y8RsZteEBt8w9DeiQyJ50hBs37vm +ExH8nYQKE3vwO9D8owrXieqWfo1IhR5kX9tUoqzVegJ5a9KK8GfaZXINFHDk6Y54jzJ0fFfy1tb0 +Nokb+Clsi7n2l9GkLqq+CxnCRelwXQIDAJ3Zo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB +/wQEAwIBBjAdBgNVHQ4EFgQU587GT/wWZ5b6SqMHwQSny2re2kcwHwYDVR0jBBgwFoAU587GT/wW +Z5b6SqMHwQSny2re2kcwDQYJKoZIhvcNAQEFBQADggIBAJuYml2+8ygjdsZs93/mQJ7ANtyVDR2t +FcU22NU57/IeIl6zgrRdu0waypIN30ckHrMk2pGI6YNw3ZPX6bqz3xZaPt7gyPvT/Wwp+BVGoGgm +zJNSroIBk5DKd8pNSe/iWtkqvTDOTLKBtjDOWU/aWR1qeqRFsIImgYZ29fUQALjuswnoT4cCB64k +XPBfrAowzIpAoHMEwfuJJPaaHFy3PApnNgUIMbOv2AFoKuB4j3TeuFGkjGwgPaL7s9QJ/XvCgKqT +bCmYIai7FvOpEl90tYeY8pUm3zTvilORiF0alKM/fCL414i6poyWqD1SNGKfAB5UVUJnxk1Gj7sU +RT0KlhaOEKGXmdXTMIXM3rRyt7yKPBgpaP3ccQfuJDlq+u2lrDgv+R4QDgZxGhBM/nV+/x5XOULK +1+EVoVZVWRvRo68R2E7DpSvvkL/A7IITW43WciyTTo9qKd+FPNMN4KIYEsxVL0e3p5sC/kH2iExt +2qkBR4NkJ2IQgtYSe14DHzSpyZH+r11thie3I6p1GMog57AP14kOpmciY/SDQSsGS7tY1dHXt7kQ +Y9iJSrSq3RZj9W6+YKH47ejWkE8axsWgKdOnIaj1Wjz3x0miIZpKlVIglnKaZsv30oZDfCK+lvm9 +AahH3eU7QPl1K5srRmSGjR70j/sHd9DqSaIcjVIUpgqT +-----END CERTIFICATE----- + +certSIGN ROOT CA +================ +-----BEGIN CERTIFICATE----- +MIIDODCCAiCgAwIBAgIGIAYFFnACMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNVBAYTAlJPMREwDwYD +VQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBDQTAeFw0wNjA3MDQxNzIwMDRa +Fw0zMTA3MDQxNzIwMDRaMDsxCzAJBgNVBAYTAlJPMREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UE +CxMQY2VydFNJR04gUk9PVCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALczuX7I +JUqOtdu0KBuqV5Do0SLTZLrTk+jUrIZhQGpgV2hUhE28alQCBf/fm5oqrl0Hj0rDKH/v+yv6efHH +rfAQUySQi2bJqIirr1qjAOm+ukbuW3N7LBeCgV5iLKECZbO9xSsAfsT8AzNXDe3i+s5dRdY4zTW2 +ssHQnIFKquSyAVwdj1+ZxLGt24gh65AIgoDzMKND5pCCrlUoSe1b16kQOA7+j0xbm0bqQfWwCHTD +0IgztnzXdN/chNFDDnU5oSVAKOp4yw4sLjmdjItuFhwvJoIQ4uNllAoEwF73XVv4EOLQunpL+943 +AAAaWyjj0pxzPjKHmKHJUS/X3qwzs08CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B +Af8EBAMCAcYwHQYDVR0OBBYEFOCMm9slSbPxfIbWskKHC9BroNnkMA0GCSqGSIb3DQEBBQUAA4IB +AQA+0hyJLjX8+HXd5n9liPRyTMks1zJO890ZeUe9jjtbkw9QSSQTaxQGcu8J06Gh40CEyecYMnQ8 +SG4Pn0vU9x7Tk4ZkVJdjclDVVc/6IJMCopvDI5NOFlV2oHB5bc0hH88vLbwZ44gx+FkagQnIl6Z0 +x2DEW8xXjrJ1/RsCCdtZb3KTafcxQdaIOL+Hsr0Wefmq5L6IJd1hJyMctTEHBDa0GpC9oHRxUIlt +vBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNwi/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7Nz +TogVZ96edhBiIL5VaZVDADlN9u6wWk5JRFRYX0KD +-----END CERTIFICATE----- + +CNNIC ROOT +========== +-----BEGIN CERTIFICATE----- +MIIDVTCCAj2gAwIBAgIESTMAATANBgkqhkiG9w0BAQUFADAyMQswCQYDVQQGEwJDTjEOMAwGA1UE +ChMFQ05OSUMxEzARBgNVBAMTCkNOTklDIFJPT1QwHhcNMDcwNDE2MDcwOTE0WhcNMjcwNDE2MDcw +OTE0WjAyMQswCQYDVQQGEwJDTjEOMAwGA1UEChMFQ05OSUMxEzARBgNVBAMTCkNOTklDIFJPT1Qw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDTNfc/c3et6FtzF8LRb+1VvG7q6KR5smzD +o+/hn7E7SIX1mlwhIhAsxYLO2uOabjfhhyzcuQxauohV3/2q2x8x6gHx3zkBwRP9SFIhxFXf2tiz +VHa6dLG3fdfA6PZZxU3Iva0fFNrfWEQlMhkqx35+jq44sDB7R3IJMfAw28Mbdim7aXZOV/kbZKKT +VrdvmW7bCgScEeOAH8tjlBAKqeFkgjH5jCftppkA9nCTGPihNIaj3XrCGHn2emU1z5DrvTOTn1Or +czvmmzQgLx3vqR1jGqCA2wMv+SYahtKNu6m+UjqHZ0gNv7Sg2Ca+I19zN38m5pIEo3/PIKe38zrK +y5nLAgMBAAGjczBxMBEGCWCGSAGG+EIBAQQEAwIABzAfBgNVHSMEGDAWgBRl8jGtKvf33VKWCscC +wQ7vptU7ETAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIB/jAdBgNVHQ4EFgQUZfIxrSr3991S +lgrHAsEO76bVOxEwDQYJKoZIhvcNAQEFBQADggEBAEs17szkrr/Dbq2flTtLP1se31cpolnKOOK5 +Gv+e5m4y3R6u6jW39ZORTtpC4cMXYFDy0VwmuYK36m3knITnA3kXr5g9lNvHugDnuL8BV8F3RTIM +O/G0HAiw/VGgod2aHRM2mm23xzy54cXZF/qD1T0VoDy7HgviyJA/qIYM/PmLXoXLT1tLYhFHxUV8 +BS9BsZ4QaRuZluBVeftOhpm4lNqGOGqTo+fLbuXf6iFViZx9fX+Y9QCJ7uOEwFyWtcVG6kbghVW2 +G8kS1sHNzYDzAgE8yGnLRUhj2JTQ7IUOO04RZfSCjKY9ri4ilAnIXOo8gV0WKgOXFlUJ24pBgp5m +mxE= +-----END CERTIFICATE----- + +ApplicationCA - Japanese Government +=================================== +-----BEGIN CERTIFICATE----- +MIIDoDCCAoigAwIBAgIBMTANBgkqhkiG9w0BAQUFADBDMQswCQYDVQQGEwJKUDEcMBoGA1UEChMT +SmFwYW5lc2UgR292ZXJubWVudDEWMBQGA1UECxMNQXBwbGljYXRpb25DQTAeFw0wNzEyMTIxNTAw +MDBaFw0xNzEyMTIxNTAwMDBaMEMxCzAJBgNVBAYTAkpQMRwwGgYDVQQKExNKYXBhbmVzZSBHb3Zl +cm5tZW50MRYwFAYDVQQLEw1BcHBsaWNhdGlvbkNBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEAp23gdE6Hj6UG3mii24aZS2QNcfAKBZuOquHMLtJqO8F6tJdhjYq+xpqcBrSGUeQ3DnR4 +fl+Kf5Sk10cI/VBaVuRorChzoHvpfxiSQE8tnfWuREhzNgaeZCw7NCPbXCbkcXmP1G55IrmTwcrN +wVbtiGrXoDkhBFcsovW8R0FPXjQilbUfKW1eSvNNcr5BViCH/OlQR9cwFO5cjFW6WY2H/CPek9AE +jP3vbb3QesmlOmpyM8ZKDQUXKi17safY1vC+9D/qDihtQWEjdnjDuGWk81quzMKq2edY3rZ+nYVu +nyoKb58DKTCXKB28t89UKU5RMfkntigm/qJj5kEW8DOYRwIDAQABo4GeMIGbMB0GA1UdDgQWBBRU +WssmP3HMlEYNllPqa0jQk/5CdTAOBgNVHQ8BAf8EBAMCAQYwWQYDVR0RBFIwUKROMEwxCzAJBgNV +BAYTAkpQMRgwFgYDVQQKDA/ml6XmnKzlm73mlL/lupwxIzAhBgNVBAsMGuOCouODl+ODquOCseOD +vOOCt+ODp+ODs0NBMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBADlqRHZ3ODrs +o2dGD/mLBqj7apAxzn7s2tGJfHrrLgy9mTLnsCTWw//1sogJhyzjVOGjprIIC8CFqMjSnHH2HZ9g +/DgzE+Ge3Atf2hZQKXsvcJEPmbo0NI2VdMV+eKlmXb3KIXdCEKxmJj3ekav9FfBv7WxfEPjzFvYD +io+nEhEMy/0/ecGc/WLuo89UDNErXxc+4z6/wCs+CZv+iKZ+tJIX/COUgb1up8WMwusRRdv4QcmW +dupwX3kSa+SjB1oF7ydJzyGfikwJcGapJsErEU4z0g781mzSDjJkaP+tBXhfAx2o45CsJOAPQKdL +rosot4LKGAfmt1t06SAZf7IbiVQ= +-----END CERTIFICATE----- + +GeoTrust Primary Certification Authority - G3 +============================================= +-----BEGIN CERTIFICATE----- +MIID/jCCAuagAwIBAgIQFaxulBmyeUtB9iepwxgPHzANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UE +BhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChjKSAyMDA4IEdlb1RydXN0 +IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFy +eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEczMB4XDTA4MDQwMjAwMDAwMFoXDTM3MTIwMTIz +NTk1OVowgZgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAo +YykgMjAwOCBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNVBAMT +LUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBANziXmJYHTNXOTIz+uvLh4yn1ErdBojqZI4xmKU4kB6Yzy5j +K/BGvESyiaHAKAxJcCGVn2TAppMSAmUmhsalifD614SgcK9PGpc/BkTVyetyEH3kMSj7HGHmKAdE +c5IiaacDiGydY8hS2pgn5whMcD60yRLBxWeDXTPzAxHsatBT4tG6NmCUgLthY2xbF37fQJQeqw3C +IShwiP/WJmxsYAQlTlV+fe+/lEjetx3dcI0FX4ilm/LC7urRQEFtYjgdVgbFA0dRIBn8exALDmKu +dlW/X3e+PkkBUz2YJQN2JFodtNuJ6nnltrM7P7pMKEF/BqxqjsHQ9gUdfeZChuOl1UcCAwEAAaNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMR5yo6hTgMdHNxr +2zFblD4/MH8tMA0GCSqGSIb3DQEBCwUAA4IBAQAtxRPPVoB7eni9n64smefv2t+UXglpp+duaIy9 +cr5HqQ6XErhK8WTTOd8lNNTBzU6B8A8ExCSzNJbGpqow32hhc9f5joWJ7w5elShKKiePEI4ufIbE +Ap7aDHdlDkQNkv39sxY2+hENHYwOB4lqKVb3cvTdFZx3NWZXqxNT2I7BQMXXExZacse3aQHEerGD +AWh9jUGhlBjBJVz88P6DAod8DQ3PLghcSkANPuyBYeYk28rgDi0Hsj5W3I31QYUHSJsMC8tJP33s +t/3LjWeJGqvtux6jAAgIFyqCXDFdRootD4abdNlF+9RAsXqqaC2Gspki4cErx5z481+oghLrGREt +-----END CERTIFICATE----- + +thawte Primary Root CA - G2 +=========================== +-----BEGIN CERTIFICATE----- +MIICiDCCAg2gAwIBAgIQNfwmXNmET8k9Jj1Xm67XVjAKBggqhkjOPQQDAzCBhDELMAkGA1UEBhMC +VVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjE4MDYGA1UECxMvKGMpIDIwMDcgdGhhd3RlLCBJbmMu +IC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAiBgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3Qg +Q0EgLSBHMjAeFw0wNzExMDUwMDAwMDBaFw0zODAxMTgyMzU5NTlaMIGEMQswCQYDVQQGEwJVUzEV +MBMGA1UEChMMdGhhd3RlLCBJbmMuMTgwNgYDVQQLEy8oYykgMjAwNyB0aGF3dGUsIEluYy4gLSBG +b3IgYXV0aG9yaXplZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAt +IEcyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEotWcgnuVnfFSeIf+iha/BebfowJPDQfGAFG6DAJS +LSKkQjnE/o/qycG+1E3/n3qe4rF8mq2nhglzh9HnmuN6papu+7qzcMBniKI11KOasf2twu8x+qi5 +8/sIxpHR+ymVo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU +mtgAMADna3+FGO6Lts6KDPgR4bswCgYIKoZIzj0EAwMDaQAwZgIxAN344FdHW6fmCsO99YCKlzUN +G4k8VIZ3KMqh9HneteY4sPBlcIx/AlTCv//YoT7ZzwIxAMSNlPzcU9LcnXgWHxUzI1NS41oxXZ3K +rr0TKUQNJ1uo52icEvdYPy5yAlejj6EULg== +-----END CERTIFICATE----- + +thawte Primary Root CA - G3 +=========================== +-----BEGIN CERTIFICATE----- +MIIEKjCCAxKgAwIBAgIQYAGXt0an6rS0mtZLL/eQ+zANBgkqhkiG9w0BAQsFADCBrjELMAkGA1UE +BhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2 +aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIwMDggdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhv +cml6ZWQgdXNlIG9ubHkxJDAiBgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMzAeFw0w +ODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIGuMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhh +d3RlLCBJbmMuMSgwJgYDVQQLEx9DZXJ0aWZpY2F0aW9uIFNlcnZpY2VzIERpdmlzaW9uMTgwNgYD +VQQLEy8oYykgMjAwOCB0aGF3dGUsIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTEkMCIG +A1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAtIEczMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAsr8nLPvb2FvdeHsbnndmgcs+vHyu86YnmjSjaDFxODNi5PNxZnmxqWWjpYvVj2At +P0LMqmsywCPLLEHd5N/8YZzic7IilRFDGF/Eth9XbAoFWCLINkw6fKXRz4aviKdEAhN0cXMKQlkC ++BsUa0Lfb1+6a4KinVvnSr0eAXLbS3ToO39/fR8EtCab4LRarEc9VbjXsCZSKAExQGbY2SS99irY +7CFJXJv2eul/VTV+lmuNk5Mny5K76qxAwJ/C+IDPXfRa3M50hqY+bAtTyr2SzhkGcuYMXDhpxwTW +vGzOW/b3aJzcJRVIiKHpqfiYnODz1TEoYRFsZ5aNOZnLwkUkOQIDAQABo0IwQDAPBgNVHRMBAf8E +BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUrWyqlGCc7eT/+j4KdCtjA/e2Wb8wDQYJ +KoZIhvcNAQELBQADggEBABpA2JVlrAmSicY59BDlqQ5mU1143vokkbvnRFHfxhY0Cu9qRFHqKweK +A3rD6z8KLFIWoCtDuSWQP3CpMyVtRRooOyfPqsMpQhvfO0zAMzRbQYi/aytlryjvsvXDqmbOe1bu +t8jLZ8HJnBoYuMTDSQPxYA5QzUbF83d597YV4Djbxy8ooAw/dyZ02SUS2jHaGh7cKUGRIjxpp7sC +8rZcJwOJ9Abqm+RyguOhCcHpABnTPtRwa7pxpqpYrvS76Wy274fMm7v/OeZWYdMKp8RcTGB7BXcm +er/YB1IsYvdwY9k5vG8cwnncdimvzsUsZAReiDZuMdRAGmI0Nj81Aa6sY6A= +-----END CERTIFICATE----- + +GeoTrust Primary Certification Authority - G2 +============================================= +-----BEGIN CERTIFICATE----- +MIICrjCCAjWgAwIBAgIQPLL0SAoA4v7rJDteYD7DazAKBggqhkjOPQQDAzCBmDELMAkGA1UEBhMC +VVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChjKSAyMDA3IEdlb1RydXN0IElu +Yy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBD +ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMB4XDTA3MTEwNTAwMDAwMFoXDTM4MDExODIzNTk1 +OVowgZgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykg +MjAwNyBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNVBAMTLUdl +b1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMjB2MBAGByqGSM49AgEG +BSuBBAAiA2IABBWx6P0DFUPlrOuHNxFi79KDNlJ9RVcLSo17VDs6bl8VAsBQps8lL33KSLjHUGMc +KiEIfJo22Av+0SbFWDEwKCXzXV2juLaltJLtbCyf691DiaI8S0iRHVDsJt/WYC69IaNCMEAwDwYD +VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBVfNVdRVfslsq0DafwBo/q+ +EVXVMAoGCCqGSM49BAMDA2cAMGQCMGSWWaboCd6LuvpaiIjwH5HTRqjySkwCY/tsXzjbLkGTqQ7m +ndwxHLKgpxgceeHHNgIwOlavmnRs9vuD4DPTCF+hnMJbn0bWtsuRBmOiBuczrD6ogRLQy7rQkgu2 +npaqBA+K +-----END CERTIFICATE----- + +VeriSign Universal Root Certification Authority +=============================================== +-----BEGIN CERTIFICATE----- +MIIEuTCCA6GgAwIBAgIQQBrEZCGzEyEDDrvkEhrFHTANBgkqhkiG9w0BAQsFADCBvTELMAkGA1UE +BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBO +ZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwOCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVk +IHVzZSBvbmx5MTgwNgYDVQQDEy9WZXJpU2lnbiBVbml2ZXJzYWwgUm9vdCBDZXJ0aWZpY2F0aW9u +IEF1dGhvcml0eTAeFw0wODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIG9MQswCQYDVQQGEwJV +UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv +cmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl +IG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNhbCBSb290IENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx2E3XrEBNNti1xWb/1hajCMj +1mCOkdeQmIN65lgZOIzF9uVkhbSicfvtvbnazU0AtMgtc6XHaXGVHzk8skQHnOgO+k1KxCHfKWGP +MiJhgsWHH26MfF8WIFFE0XBPV+rjHOPMee5Y2A7Cs0WTwCznmhcrewA3ekEzeOEz4vMQGn+HLL72 +9fdC4uW/h2KJXwBL38Xd5HVEMkE6HnFuacsLdUYI0crSK5XQz/u5QGtkjFdN/BMReYTtXlT2NJ8I +AfMQJQYXStrxHXpma5hgZqTZ79IugvHw7wnqRMkVauIDbjPTrJ9VAMf2CGqUuV/c4DPxhGD5WycR +tPwW8rtWaoAljQIDAQABo4GyMIGvMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMG0G +CCsGAQUFBwEMBGEwX6FdoFswWTBXMFUWCWltYWdlL2dpZjAhMB8wBwYFKw4DAhoEFI/l0xqGrI2O +a8PPgGrUSBgsexkuMCUWI2h0dHA6Ly9sb2dvLnZlcmlzaWduLmNvbS92c2xvZ28uZ2lmMB0GA1Ud +DgQWBBS2d/ppSEefUxLVwuoHMnYH0ZcHGTANBgkqhkiG9w0BAQsFAAOCAQEASvj4sAPmLGd75JR3 +Y8xuTPl9Dg3cyLk1uXBPY/ok+myDjEedO2Pzmvl2MpWRsXe8rJq+seQxIcaBlVZaDrHC1LGmWazx +Y8u4TB1ZkErvkBYoH1quEPuBUDgMbMzxPcP1Y+Oz4yHJJDnp/RVmRvQbEdBNc6N9Rvk97ahfYtTx +P/jgdFcrGJ2BtMQo2pSXpXDrrB2+BxHw1dvd5Yzw1TKwg+ZX4o+/vqGqvz0dtdQ46tewXDpPaj+P +wGZsY6rp2aQW9IHRlRQOfc2VNNnSj3BzgXucfr2YYdhFh5iQxeuGMMY1v/D/w1WIg0vvBZIGcfK4 +mJO37M2CYfE45k+XmCpajQ== +-----END CERTIFICATE----- + +VeriSign Class 3 Public Primary Certification Authority - G4 +============================================================ +-----BEGIN CERTIFICATE----- +MIIDhDCCAwqgAwIBAgIQL4D+I4wOIg9IZxIokYesszAKBggqhkjOPQQDAzCByjELMAkGA1UEBhMC +VVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3 +b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVz +ZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5IC0gRzQwHhcNMDcxMTA1MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCByjEL +MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBU +cnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRo +b3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5 +IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASnVnp8 +Utpkmw4tXNherJI9/gHmGUo9FANL+mAnINmDiWn6VMaaGF5VKmTeBvaNSjutEDxlPZCIBIngMGGz +rl0Bp3vefLK+ymVhAIau2o970ImtTR1ZmkGxvEeA3J5iw/mjgbIwga8wDwYDVR0TAQH/BAUwAwEB +/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEw +HzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVyaXNpZ24u +Y29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFLMWkf3upm7ktS5Jj4d4gYDs5bG1MAoGCCqGSM49BAMD +A2gAMGUCMGYhDBgmYFo4e1ZC4Kf8NoRRkSAsdk1DPcQdhCPQrNZ8NQbOzWm9kA3bbEhCHQ6qQgIx +AJw9SDkjOVgaFRJZap7v1VmyHVIsmXHNxynfGyphe3HR3vPA5Q06Sqotp9iGKt0uEA== +-----END CERTIFICATE----- + +NetLock Arany (Class Gold) Főtanúsítvány +============================================ +-----BEGIN CERTIFICATE----- +MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQGEwJIVTERMA8G +A1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3MDUGA1UECwwuVGFuw7pzw610 +dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBB +cmFueSAoQ2xhc3MgR29sZCkgRsWRdGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgx +MjA2MTUwODIxWjCBpzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxO +ZXRMb2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlmaWNhdGlv +biBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNzIEdvbGQpIEbFkXRhbsO6 +c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxCRec75LbRTDofTjl5Bu +0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrTlF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw +/HpYzY6b7cNGbIRwXdrzAZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAk +H3B5r9s5VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRGILdw +fzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2BJtr+UBdADTHLpl1 +neWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAGAQH/AgEEMA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2MU9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwW +qZw8UQCgwBEIBaeZ5m8BiFRhbvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTta +YtOUZcTh5m2C+C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC +bLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2FuLjbvrW5Kfna +NwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2XjG4Kvte9nHfRCaexOYNkbQu +dZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E= +-----END CERTIFICATE----- + +Staat der Nederlanden Root CA - G2 +================================== +-----BEGIN CERTIFICATE----- +MIIFyjCCA7KgAwIBAgIEAJiWjDANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJOTDEeMBwGA1UE +CgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFhdCBkZXIgTmVkZXJsYW5kZW4g +Um9vdCBDQSAtIEcyMB4XDTA4MDMyNjExMTgxN1oXDTIwMDMyNTExMDMxMFowWjELMAkGA1UEBhMC +TkwxHjAcBgNVBAoMFVN0YWF0IGRlciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5l +ZGVybGFuZGVuIFJvb3QgQ0EgLSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMVZ +5291qj5LnLW4rJ4L5PnZyqtdj7U5EILXr1HgO+EASGrP2uEGQxGZqhQlEq0i6ABtQ8SpuOUfiUtn +vWFI7/3S4GCI5bkYYCjDdyutsDeqN95kWSpGV+RLufg3fNU254DBtvPUZ5uW6M7XxgpT0GtJlvOj +CwV3SPcl5XCsMBQgJeN/dVrlSPhOewMHBPqCYYdu8DvEpMfQ9XQ+pV0aCPKbJdL2rAQmPlU6Yiil +e7Iwr/g3wtG61jj99O9JMDeZJiFIhQGp5Rbn3JBV3w/oOM2ZNyFPXfUib2rFEhZgF1XyZWampzCR +OME4HYYEhLoaJXhena/MUGDWE4dS7WMfbWV9whUYdMrhfmQpjHLYFhN9C0lK8SgbIHRrxT3dsKpI +CT0ugpTNGmXZK4iambwYfp/ufWZ8Pr2UuIHOzZgweMFvZ9C+X+Bo7d7iscksWXiSqt8rYGPy5V65 +48r6f1CGPqI0GAwJaCgRHOThuVw+R7oyPxjMW4T182t0xHJ04eOLoEq9jWYv6q012iDTiIJh8BIi +trzQ1aTsr1SIJSQ8p22xcik/Plemf1WvbibG/ufMQFxRRIEKeN5KzlW/HdXZt1bv8Hb/C3m1r737 +qWmRRpdogBQ2HbN/uymYNqUg+oJgYjOk7Na6B6duxc8UpufWkjTYgfX8HV2qXB72o007uPc5AgMB +AAGjgZcwgZQwDwYDVR0TAQH/BAUwAwEB/zBSBgNVHSAESzBJMEcGBFUdIAAwPzA9BggrBgEFBQcC +ARYxaHR0cDovL3d3dy5wa2lvdmVyaGVpZC5ubC9wb2xpY2llcy9yb290LXBvbGljeS1HMjAOBgNV +HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJFoMocVHYnitfGsNig0jQt8YojrMA0GCSqGSIb3DQEBCwUA +A4ICAQCoQUpnKpKBglBu4dfYszk78wIVCVBR7y29JHuIhjv5tLySCZa59sCrI2AGeYwRTlHSeYAz ++51IvuxBQ4EffkdAHOV6CMqqi3WtFMTC6GY8ggen5ieCWxjmD27ZUD6KQhgpxrRW/FYQoAUXvQwj +f/ST7ZwaUb7dRUG/kSS0H4zpX897IZmflZ85OkYcbPnNe5yQzSipx6lVu6xiNGI1E0sUOlWDuYaN +kqbG9AclVMwWVxJKgnjIFNkXgiYtXSAfea7+1HAWFpWD2DU5/1JddRwWxRNVz0fMdWVSSt7wsKfk +CpYL+63C4iWEst3kvX5ZbJvw8NjnyvLplzh+ib7M+zkXYT9y2zqR2GUBGR2tUKRXCnxLvJxxcypF +URmFzI79R6d0lR2o0a9OF7FpJsKqeFdbxU2n5Z4FF5TKsl+gSRiNNOkmbEgeqmiSBeGCc1qb3Adb +CG19ndeNIdn8FCCqwkXfP+cAslHkwvgFuXkajDTznlvkN1trSt8sV4pAWja63XVECDdCcAz+3F4h +oKOKwJCcaNpQ5kUQR3i2TtJlycM33+FCY7BXN0Ute4qcvwXqZVUz9zkQxSgqIXobisQk+T8VyJoV +IPVVYpbtbZNQvOSqeK3Zywplh6ZmwcSBo3c6WB4L7oOLnR7SUqTMHW+wmG2UMbX4cQrcufx9MmDm +66+KAQ== +-----END CERTIFICATE----- + +CA Disig +======== +-----BEGIN CERTIFICATE----- +MIIEDzCCAvegAwIBAgIBATANBgkqhkiG9w0BAQUFADBKMQswCQYDVQQGEwJTSzETMBEGA1UEBxMK +QnJhdGlzbGF2YTETMBEGA1UEChMKRGlzaWcgYS5zLjERMA8GA1UEAxMIQ0EgRGlzaWcwHhcNMDYw +MzIyMDEzOTM0WhcNMTYwMzIyMDEzOTM0WjBKMQswCQYDVQQGEwJTSzETMBEGA1UEBxMKQnJhdGlz +bGF2YTETMBEGA1UEChMKRGlzaWcgYS5zLjERMA8GA1UEAxMIQ0EgRGlzaWcwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQCS9jHBfYj9mQGp2HvycXXxMcbzdWb6UShGhJd4NLxs/LxFWYgm +GErENx+hSkS943EE9UQX4j/8SFhvXJ56CbpRNyIjZkMhsDxkovhqFQ4/61HhVKndBpnXmjxUizkD +Pw/Fzsbrg3ICqB9x8y34dQjbYkzo+s7552oftms1grrijxaSfQUMbEYDXcDtab86wYqg6I7ZuUUo +hwjstMoVvoLdtUSLLa2GDGhibYVW8qwUYzrG0ZmsNHhWS8+2rT+MitcE5eN4TPWGqvWP+j1scaMt +ymfraHtuM6kMgiioTGohQBUgDCZbg8KpFhXAJIJdKxatymP2dACw30PEEGBWZ2NFAgMBAAGjgf8w +gfwwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUjbJJaJ1yCCW5wCf1UJNWSEZx+Y8wDgYDVR0P +AQH/BAQDAgEGMDYGA1UdEQQvMC2BE2Nhb3BlcmF0b3JAZGlzaWcuc2uGFmh0dHA6Ly93d3cuZGlz +aWcuc2svY2EwZgYDVR0fBF8wXTAtoCugKYYnaHR0cDovL3d3dy5kaXNpZy5zay9jYS9jcmwvY2Ff +ZGlzaWcuY3JsMCygKqAohiZodHRwOi8vY2EuZGlzaWcuc2svY2EvY3JsL2NhX2Rpc2lnLmNybDAa +BgNVHSAEEzARMA8GDSuBHpGT5goAAAABAQEwDQYJKoZIhvcNAQEFBQADggEBAF00dGFMrzvY/59t +WDYcPQuBDRIrRhCA/ec8J9B6yKm2fnQwM6M6int0wHl5QpNt/7EpFIKrIYwvF/k/Ji/1WcbvgAa3 +mkkp7M5+cTxqEEHA9tOasnxakZzArFvITV734VP/Q3f8nktnbNfzg9Gg4H8l37iYC5oyOGwwoPP/ +CBUz91BKez6jPiCp3C9WgArtQVCwyfTssuMmRAAOb54GvCKWU3BlxFAKRmukLyeBEicTXxChds6K +ezfqwzlhA5WYOudsiCUI/HloDYd9Yvi0X/vF2Ey9WLw/Q1vUHgFNPGO+I++MzVpQuGhU+QqZMxEA +4Z7CRneC9VkGjCFMhwnN5ag= +-----END CERTIFICATE----- + +Juur-SK +======= +-----BEGIN CERTIFICATE----- +MIIE5jCCA86gAwIBAgIEO45L/DANBgkqhkiG9w0BAQUFADBdMRgwFgYJKoZIhvcNAQkBFglwa2lA +c2suZWUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKExlBUyBTZXJ0aWZpdHNlZXJpbWlza2Vza3VzMRAw +DgYDVQQDEwdKdXVyLVNLMB4XDTAxMDgzMDE0MjMwMVoXDTE2MDgyNjE0MjMwMVowXTEYMBYGCSqG +SIb3DQEJARYJcGtpQHNrLmVlMQswCQYDVQQGEwJFRTEiMCAGA1UEChMZQVMgU2VydGlmaXRzZWVy +aW1pc2tlc2t1czEQMA4GA1UEAxMHSnV1ci1TSzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAIFxNj4zB9bjMI0TfncyRsvPGbJgMUaXhvSYRqTCZUXP00B841oiqBB4M8yIsdOBSvZiF3tf +TQou0M+LI+5PAk676w7KvRhj6IAcjeEcjT3g/1tf6mTll+g/mX8MCgkzABpTpyHhOEvWgxutr2TC ++Rx6jGZITWYfGAriPrsfB2WThbkasLnE+w0R9vXW+RvHLCu3GFH+4Hv2qEivbDtPL+/40UceJlfw +UR0zlv/vWT3aTdEVNMfqPxZIe5EcgEMPPbgFPtGzlc3Yyg/CQ2fbt5PgIoIuvvVoKIO5wTtpeyDa +Tpxt4brNj3pssAki14sL2xzVWiZbDcDq5WDQn/413z8CAwEAAaOCAawwggGoMA8GA1UdEwEB/wQF +MAMBAf8wggEWBgNVHSAEggENMIIBCTCCAQUGCisGAQQBzh8BAQEwgfYwgdAGCCsGAQUFBwICMIHD +HoHAAFMAZQBlACAAcwBlAHIAdABpAGYAaQBrAGEAYQB0ACAAbwBuACAAdgDkAGwAagBhAHMAdABh +AHQAdQBkACAAQQBTAC0AaQBzACAAUwBlAHIAdABpAGYAaQB0AHMAZQBlAHIAaQBtAGkAcwBrAGUA +cwBrAHUAcwAgAGEAbABhAG0ALQBTAEsAIABzAGUAcgB0AGkAZgBpAGsAYQBhAHQAaQBkAGUAIABr +AGkAbgBuAGkAdABhAG0AaQBzAGUAawBzMCEGCCsGAQUFBwIBFhVodHRwOi8vd3d3LnNrLmVlL2Nw +cy8wKwYDVR0fBCQwIjAgoB6gHIYaaHR0cDovL3d3dy5zay5lZS9qdXVyL2NybC8wHQYDVR0OBBYE +FASqekej5ImvGs8KQKcYP2/v6X2+MB8GA1UdIwQYMBaAFASqekej5ImvGs8KQKcYP2/v6X2+MA4G +A1UdDwEB/wQEAwIB5jANBgkqhkiG9w0BAQUFAAOCAQEAe8EYlFOiCfP+JmeaUOTDBS8rNXiRTHyo +ERF5TElZrMj3hWVcRrs7EKACr81Ptcw2Kuxd/u+gkcm2k298gFTsxwhwDY77guwqYHhpNjbRxZyL +abVAyJRld/JXIWY7zoVAtjNjGr95HvxcHdMdkxuLDF2FvZkwMhgJkVLpfKG6/2SSmuz+Ne6ML678 +IIbsSt4beDI3poHSna9aEhbKmVv8b20OxaAehsmR0FyYgl9jDIpaq9iVpszLita/ZEuOyoqysOkh +Mp6qqIWYNIE5ITuoOlIyPfZrN4YGWhWY3PARZv40ILcD9EEQfTmEeZZyY7aWAuVrua0ZTbvGRNs2 +yyqcjg== +-----END CERTIFICATE----- + +Hongkong Post Root CA 1 +======================= +-----BEGIN CERTIFICATE----- +MIIDMDCCAhigAwIBAgICA+gwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoT +DUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMB4XDTAzMDUx +NTA1MTMxNFoXDTIzMDUxNTA0NTIyOVowRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoTDUhvbmdrb25n +IFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEArP84tulmAknjorThkPlAj3n54r15/gK97iSSHSL22oVyaf7XPwnU3ZG1 +ApzQjVrhVcNQhrkpJsLj2aDxaQMoIIBFIi1WpztUlVYiWR8o3x8gPW2iNr4joLFutbEnPzlTCeqr +auh0ssJlXI6/fMN4hM2eFvz1Lk8gKgifd/PFHsSaUmYeSF7jEAaPIpjhZY4bXSNmO7ilMlHIhqqh +qZ5/dpTCpmy3QfDVyAY45tQM4vM7TG1QjMSDJ8EThFk9nnV0ttgCXjqQesBCNnLsak3c78QA3xMY +V18meMjWCnl3v/evt3a5pQuEF10Q6m/hq5URX208o1xNg1vysxmKgIsLhwIDAQABoyYwJDASBgNV +HRMBAf8ECDAGAQH/AgEDMA4GA1UdDwEB/wQEAwIBxjANBgkqhkiG9w0BAQUFAAOCAQEADkbVPK7i +h9legYsCmEEIjEy82tvuJxuC52pF7BaLT4Wg87JwvVqWuspube5Gi27nKi6Wsxkz67SfqLI37pio +l7Yutmcn1KZJ/RyTZXaeQi/cImyaT/JaFTmxcdcrUehtHJjA2Sr0oYJ71clBoiMBdDhViw+5Lmei +IAQ32pwL0xch4I+XeTRvhEgCIDMb5jREn5Fw9IBehEPCKdJsEhTkYY2sEJCehFC78JZvRZ+K88ps +T/oROhUVRsPNH4NbLUES7VBnQRM9IauUiqpOfMGx+6fWtScvl6tu4B3i0RwsH0Ti/L6RoZz71ilT +c4afU9hDDl3WY4JxHYB0yvbiAmvZWg== +-----END CERTIFICATE----- + +SecureSign RootCA11 +=================== +-----BEGIN CERTIFICATE----- +MIIDbTCCAlWgAwIBAgIBATANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJKUDErMCkGA1UEChMi +SmFwYW4gQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcywgSW5jLjEcMBoGA1UEAxMTU2VjdXJlU2lnbiBS +b290Q0ExMTAeFw0wOTA0MDgwNDU2NDdaFw0yOTA0MDgwNDU2NDdaMFgxCzAJBgNVBAYTAkpQMSsw +KQYDVQQKEyJKYXBhbiBDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzLCBJbmMuMRwwGgYDVQQDExNTZWN1 +cmVTaWduIFJvb3RDQTExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA/XeqpRyQBTvL +TJszi1oURaTnkBbR31fSIRCkF/3frNYfp+TbfPfs37gD2pRY/V1yfIw/XwFndBWW4wI8h9uuywGO +wvNmxoVF9ALGOrVisq/6nL+k5tSAMJjzDbaTj6nU2DbysPyKyiyhFTOVMdrAG/LuYpmGYz+/3ZMq +g6h2uRMft85OQoWPIucuGvKVCbIFtUROd6EgvanyTgp9UK31BQ1FT0Zx/Sg+U/sE2C3XZR1KG/rP +O7AxmjVuyIsG0wCR8pQIZUyxNAYAeoni8McDWc/V1uinMrPmmECGxc0nEovMe863ETxiYAcjPitA +bpSACW22s293bzUIUPsCh8U+iQIDAQABo0IwQDAdBgNVHQ4EFgQUW/hNT7KlhtQ60vFjmqC+CfZX +t94wDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAKCh +OBZmLqdWHyGcBvod7bkixTgm2E5P7KN/ed5GIaGHd48HCJqypMWvDzKYC3xmKbabfSVSSUOrTC4r +bnpwrxYO4wJs+0LmGJ1F2FXI6Dvd5+H0LgscNFxsWEr7jIhQX5Ucv+2rIrVls4W6ng+4reV6G4pQ +Oh29Dbx7VFALuUKvVaAYga1lme++5Jy/xIWrQbJUb9wlze144o4MjQlJ3WN7WmmWAiGovVJZ6X01 +y8hSyn+B/tlr0/cR7SXf+Of5pPpyl4RTDaXQMhhRdlkUbA/r7F+AjHVDg8OFmP9Mni0N5HeDk061 +lgeLKBObjBmNQSdJQO7e5iNEOdyhIta6A/I= +-----END CERTIFICATE----- + +ACEDICOM Root +============= +-----BEGIN CERTIFICATE----- +MIIFtTCCA52gAwIBAgIIYY3HhjsBggUwDQYJKoZIhvcNAQEFBQAwRDEWMBQGA1UEAwwNQUNFRElD +T00gUm9vdDEMMAoGA1UECwwDUEtJMQ8wDQYDVQQKDAZFRElDT00xCzAJBgNVBAYTAkVTMB4XDTA4 +MDQxODE2MjQyMloXDTI4MDQxMzE2MjQyMlowRDEWMBQGA1UEAwwNQUNFRElDT00gUm9vdDEMMAoG +A1UECwwDUEtJMQ8wDQYDVQQKDAZFRElDT00xCzAJBgNVBAYTAkVTMIICIjANBgkqhkiG9w0BAQEF +AAOCAg8AMIICCgKCAgEA/5KV4WgGdrQsyFhIyv2AVClVYyT/kGWbEHV7w2rbYgIB8hiGtXxaOLHk +WLn709gtn70yN78sFW2+tfQh0hOR2QetAQXW8713zl9CgQr5auODAKgrLlUTY4HKRxx7XBZXehuD +YAQ6PmXDzQHe3qTWDLqO3tkE7hdWIpuPY/1NFgu3e3eM+SW10W2ZEi5PGrjm6gSSrj0RuVFCPYew +MYWveVqc/udOXpJPQ/yrOq2lEiZmueIM15jO1FillUAKt0SdE3QrwqXrIhWYENiLxQSfHY9g5QYb +m8+5eaA9oiM/Qj9r+hwDezCNzmzAv+YbX79nuIQZ1RXve8uQNjFiybwCq0Zfm/4aaJQ0PZCOrfbk +HQl/Sog4P75n/TSW9R28MHTLOO7VbKvU/PQAtwBbhTIWdjPp2KOZnQUAqhbm84F9b32qhm2tFXTT +xKJxqvQUfecyuB+81fFOvW8XAjnXDpVCOscAPukmYxHqC9FK/xidstd7LzrZlvvoHpKuE1XI2Sf2 +3EgbsCTBheN3nZqk8wwRHQ3ItBTutYJXCb8gWH8vIiPYcMt5bMlL8qkqyPyHK9caUPgn6C9D4zq9 +2Fdx/c6mUlv53U3t5fZvie27k5x2IXXwkkwp9y+cAS7+UEaeZAwUswdbxcJzbPEHXEUkFDWug/Fq +TYl6+rPYLWbwNof1K1MCAwEAAaOBqjCBpzAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKaz +4SsrSbbXc6GqlPUB53NlTKxQMA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUprPhKytJttdzoaqU +9QHnc2VMrFAwRAYDVR0gBD0wOzA5BgRVHSAAMDEwLwYIKwYBBQUHAgEWI2h0dHA6Ly9hY2VkaWNv +bS5lZGljb21ncm91cC5jb20vZG9jMA0GCSqGSIb3DQEBBQUAA4ICAQDOLAtSUWImfQwng4/F9tqg +aHtPkl7qpHMyEVNEskTLnewPeUKzEKbHDZ3Ltvo/Onzqv4hTGzz3gvoFNTPhNahXwOf9jU8/kzJP +eGYDdwdY6ZXIfj7QeQCM8htRM5u8lOk6e25SLTKeI6RF+7YuE7CLGLHdztUdp0J/Vb77W7tH1Pwk +zQSulgUV1qzOMPPKC8W64iLgpq0i5ALudBF/TP94HTXa5gI06xgSYXcGCRZj6hitoocf8seACQl1 +ThCojz2GuHURwCRiipZ7SkXp7FnFvmuD5uHorLUwHv4FB4D54SMNUI8FmP8sX+g7tq3PgbUhh8oI +KiMnMCArz+2UW6yyetLHKKGKC5tNSixthT8Jcjxn4tncB7rrZXtaAWPWkFtPF2Y9fwsZo5NjEFIq +nxQWWOLcpfShFosOkYuByptZ+thrkQdlVV9SH686+5DdaaVbnG0OLLb6zqylfDJKZ0DcMDQj3dcE +I2bw/FWAp/tmGYI1Z2JwOV5vx+qQQEQIHriy1tvuWacNGHk0vFQYXlPKNFHtRQrmjseCNj6nOGOp +MCwXEGCSn1WHElkQwg9naRHMTh5+Spqtr0CodaxWkHS4oJyleW/c6RrIaQXpuvoDs3zk4E7Czp3o +tkYNbn5XOmeUwssfnHdKZ05phkOTOPu220+DkdRgfks+KzgHVZhepA== +-----END CERTIFICATE----- + +Verisign Class 3 Public Primary Certification Authority +======================================================= +-----BEGIN CERTIFICATE----- +MIICPDCCAaUCEDyRMcsf9tAbDpq40ES/Er4wDQYJKoZIhvcNAQEFBQAwXzELMAkGA1UEBhMCVVMx +FzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmltYXJ5 +IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2MDEyOTAwMDAwMFoXDTI4MDgwMjIzNTk1OVow +XzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAz +IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUA +A4GNADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhEBarsAx94 +f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/isI19wKTakyYbnsZogy1Ol +hec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBABByUqkFFBky +CEHwxWsKzH4PIRnN5GfcX6kb5sroc50i2JhucwNhkcV8sEVAbkSdjbCxlnRhLQ2pRdKkkirWmnWX +bj9T/UWZYB2oK0z5XqcJ2HUw19JlYD1n1khVdWk/kfVIC0dpImmClr7JyDiGSnoscxlIaU5rfGW/ +D/xwzoiQ +-----END CERTIFICATE----- + +Microsec e-Szigno Root CA 2009 +============================== +-----BEGIN CERTIFICATE----- +MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYDVQQGEwJIVTER +MA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jv +c2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5o +dTAeFw0wOTA2MTYxMTMwMThaFw0yOTEyMzAxMTMwMThaMIGCMQswCQYDVQQGEwJIVTERMA8GA1UE +BwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUt +U3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5odTCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG2KfgQvvPkd6mJviZpWNwrZuuyjNA +fW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tccbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG +0IMZfcChEhyVbUr02MelTTMuhTlAdX4UfIASmFDHQWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKA +pxn1ntxVUwOXewdI/5n7N4okxFnMUBBjjqqpGrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm +1HxdrtbCxkzlBQHZ7Vf8wSN5/PrIJIOV87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1+rUC +AwEAAaOBgDB+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTLD8bf +QkPMPcu1SCOhGnqmKrs0aDAfBgNVHSMEGDAWgBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAbBgNVHREE +FDASgRBpbmZvQGUtc3ppZ25vLmh1MA0GCSqGSIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0o +lZMEyL/azXm4Q5DwpL7v8u8hmLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfX +I/OMn74dseGkddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775 +tyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c2Pm2G2JwCz02 +yULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5tHMN1Rq41Bab2XD0h7lbwyYIi +LXpUq3DDfSJlgnCW +-----END CERTIFICATE----- + +E-Guven Kok Elektronik Sertifika Hizmet Saglayicisi +=================================================== +-----BEGIN CERTIFICATE----- +MIIDtjCCAp6gAwIBAgIQRJmNPMADJ72cdpW56tustTANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQG +EwJUUjEoMCYGA1UEChMfRWxla3Ryb25payBCaWxnaSBHdXZlbmxpZ2kgQS5TLjE8MDoGA1UEAxMz +ZS1HdXZlbiBLb2sgRWxla3Ryb25payBTZXJ0aWZpa2EgSGl6bWV0IFNhZ2xheWljaXNpMB4XDTA3 +MDEwNDExMzI0OFoXDTE3MDEwNDExMzI0OFowdTELMAkGA1UEBhMCVFIxKDAmBgNVBAoTH0VsZWt0 +cm9uaWsgQmlsZ2kgR3V2ZW5saWdpIEEuUy4xPDA6BgNVBAMTM2UtR3V2ZW4gS29rIEVsZWt0cm9u +aWsgU2VydGlmaWthIEhpem1ldCBTYWdsYXlpY2lzaTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBAMMSIJ6wXgBljU5Gu4Bc6SwGl9XzcslwuedLZYDBS75+PNdUMZTe1RK6UxYC6lhj71vY +8+0qGqpxSKPcEC1fX+tcS5yWCEIlKBHMilpiAVDV6wlTL/jDj/6z/P2douNffb7tC+Bg62nsM+3Y +jfsSSYMAyYuXjDtzKjKzEve5TfL0TW3H5tYmNwjy2f1rXKPlSFxYvEK+A1qBuhw1DADT9SN+cTAI +JjjcJRFHLfO6IxClv7wC90Nex/6wN1CZew+TzuZDLMN+DfIcQ2Zgy2ExR4ejT669VmxMvLz4Bcpk +9Ok0oSy1c+HCPujIyTQlCFzz7abHlJ+tiEMl1+E5YP6sOVkCAwEAAaNCMEAwDgYDVR0PAQH/BAQD +AgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFJ/uRLOU1fqRTy7ZVZoEVtstxNulMA0GCSqG +SIb3DQEBBQUAA4IBAQB/X7lTW2M9dTLn+sR0GstG30ZpHFLPqk/CaOv/gKlR6D1id4k9CnU58W5d +F4dvaAXBlGzZXd/aslnLpRCKysw5zZ/rTt5S/wzw9JKp8mxTq5vSR6AfdPebmvEvFZ96ZDAYBzwq +D2fK/A+JYZ1lpTzlvBNbCNvj/+27BrtqBrF6T2XGgv0enIu1De5Iu7i9qgi0+6N8y5/NkHZchpZ4 +Vwpm+Vganf2XKWDeEaaQHBkc7gGWIjQ0LpH5t8Qn0Xvmv/uARFoW5evg1Ao4vOSR49XrXMGs3xtq +fJ7lddK2l4fbzIcrQzqECK+rPNv3PGYxhrCdU3nt+CPeQuMtgvEP5fqX +-----END CERTIFICATE----- + +GlobalSign Root CA - R3 +======================= +-----BEGIN CERTIFICATE----- +MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4GA1UECxMXR2xv +YmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2Jh +bFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxT +aWduIFJvb3QgQ0EgLSBSMzETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2ln +bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWt +iHL8RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsTgHeMCOFJ +0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmmKPZpO/bLyCiR5Z2KYVc3 +rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zdQQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjl +OCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZXriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2 +xmmFghcCAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE +FI/wS3+oLkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZURUm7 +lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMpjjM5RcOO5LlXbKr8 +EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK6fBdRoyV3XpYKBovHd7NADdBj+1E +bddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQXmcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18 +YIvDQVETI53O9zJrlAGomecsMx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7r +kpeDMdmztcpHWD9f +-----END CERTIFICATE----- + +TC TrustCenter Universal CA III +=============================== +-----BEGIN CERTIFICATE----- +MIID4TCCAsmgAwIBAgIOYyUAAQACFI0zFQLkbPQwDQYJKoZIhvcNAQEFBQAwezELMAkGA1UEBhMC +REUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxJDAiBgNVBAsTG1RDIFRydXN0Q2VudGVy +IFVuaXZlcnNhbCBDQTEoMCYGA1UEAxMfVEMgVHJ1c3RDZW50ZXIgVW5pdmVyc2FsIENBIElJSTAe +Fw0wOTA5MDkwODE1MjdaFw0yOTEyMzEyMzU5NTlaMHsxCzAJBgNVBAYTAkRFMRwwGgYDVQQKExNU +QyBUcnVzdENlbnRlciBHbWJIMSQwIgYDVQQLExtUQyBUcnVzdENlbnRlciBVbml2ZXJzYWwgQ0Ex +KDAmBgNVBAMTH1RDIFRydXN0Q2VudGVyIFVuaXZlcnNhbCBDQSBJSUkwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQDC2pxisLlxErALyBpXsq6DFJmzNEubkKLF5+cvAqBNLaT6hdqbJYUt +QCggbergvbFIgyIpRJ9Og+41URNzdNW88jBmlFPAQDYvDIRlzg9uwliT6CwLOunBjvvya8o84pxO +juT5fdMnnxvVZ3iHLX8LR7PH6MlIfK8vzArZQe+f/prhsq75U7Xl6UafYOPfjdN/+5Z+s7Vy+Eut +CHnNaYlAJ/Uqwa1D7KRTyGG299J5KmcYdkhtWyUB0SbFt1dpIxVbYYqt8Bst2a9c8SaQaanVDED1 +M4BDj5yjdipFtK+/fz6HP3bFzSreIMUWWMv5G/UPyw0RUmS40nZid4PxWJ//AgMBAAGjYzBhMB8G +A1UdIwQYMBaAFFbn4VslQ4Dg9ozhcbyO5YAvxEjiMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ +BAQDAgEGMB0GA1UdDgQWBBRW5+FbJUOA4PaM4XG8juWAL8RI4jANBgkqhkiG9w0BAQUFAAOCAQEA +g8ev6n9NCjw5sWi+e22JLumzCecYV42FmhfzdkJQEw/HkG8zrcVJYCtsSVgZ1OK+t7+rSbyUyKu+ +KGwWaODIl0YgoGhnYIg5IFHYaAERzqf2EQf27OysGh+yZm5WZ2B6dF7AbZc2rrUNXWZzwCUyRdhK +BgePxLcHsU0GDeGl6/R1yrqc0L2z0zIkTO5+4nYES0lT2PLpVDP85XEfPRRclkvxOvIAu2y0+pZV +CIgJwcyRGSmwIC3/yzikQOEXvnlhgP8HA4ZMTnsGnxGGjYnuJ8Tb4rwZjgvDwxPHLQNjO9Po5KIq +woIIlBZU8O8fJ5AluA0OKBtHd0e9HKgl8ZS0Zg== +-----END CERTIFICATE----- + +Autoridad de Certificacion Firmaprofesional CIF A62634068 +========================================================= +-----BEGIN CERTIFICATE----- +MIIGFDCCA/ygAwIBAgIIU+w77vuySF8wDQYJKoZIhvcNAQEFBQAwUTELMAkGA1UEBhMCRVMxQjBA +BgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2 +MjYzNDA2ODAeFw0wOTA1MjAwODM4MTVaFw0zMDEyMzEwODM4MTVaMFExCzAJBgNVBAYTAkVTMUIw +QAYDVQQDDDlBdXRvcmlkYWQgZGUgQ2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBB +NjI2MzQwNjgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDD +Utd9thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQMcas9UX4P +B99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefGL9ItWY16Ck6WaVICqjaY +7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15iNA9wBj4gGFrO93IbJWyTdBSTo3OxDqqH +ECNZXyAFGUftaI6SEspd/NYrspI8IM/hX68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyI +plD9amML9ZMWGxmPsu2bm8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctX +MbScyJCyZ/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirjaEbsX +LZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/TKI8xWVvTyQKmtFLK +bpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF6NkBiDkal4ZkQdU7hwxu+g/GvUgU +vzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVhOSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMBIGA1Ud +EwEB/wQIMAYBAf8CAQEwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRlzeurNR4APn7VdMActHNH +DhpkLzCBpgYDVR0gBIGeMIGbMIGYBgRVHSAAMIGPMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmZp +cm1hcHJvZmVzaW9uYWwuY29tL2NwczBcBggrBgEFBQcCAjBQHk4AUABhAHMAZQBvACAAZABlACAA +bABhACAAQgBvAG4AYQBuAG8AdgBhACAANAA3ACAAQgBhAHIAYwBlAGwAbwBuAGEAIAAwADgAMAAx +ADcwDQYJKoZIhvcNAQEFBQADggIBABd9oPm03cXF661LJLWhAqvdpYhKsg9VSytXjDvlMd3+xDLx +51tkljYyGOylMnfX40S2wBEqgLk9am58m9Ot/MPWo+ZkKXzR4Tgegiv/J2Wv+xYVxC5xhOW1//qk +R71kMrv2JYSiJ0L1ILDCExARzRAVukKQKtJE4ZYm6zFIEv0q2skGz3QeqUvVhyj5eTSSPi5E6PaP +T481PyWzOdxjKpBrIF/EUhJOlywqrJ2X3kjyo2bbwtKDlaZmp54lD+kLM5FlClrD2VQS3a/DTg4f +Jl4N3LON7NWBcN7STyQF82xO9UxJZo3R/9ILJUFI/lGExkKvgATP0H5kSeTy36LssUzAKh3ntLFl +osS88Zj0qnAHY7S42jtM+kAiMFsRpvAFDsYCA0irhpuF3dvd6qJ2gHN99ZwExEWN57kci57q13XR +crHedUTnQn3iV2t93Jm8PYMo6oCTjcVMZcFwgbg4/EMxsvYDNEeyrPsiBsse3RdHHF9mudMaotoR +saS8I8nkvof/uZS2+F0gStRf571oe2XyFR7SOqkt6dhrJKyXWERHrVkY8SFlcN7ONGCoQPHzPKTD +KCOM/iczQ0CgFzzr6juwcqajuUpLXhZI9LK8yIySxZ2frHI2vDSANGupi5LAuBft7HZT9SQBjLMi +6Et8Vcad+qMUu2WFbm5PEn4KPJ2V +-----END CERTIFICATE----- + +Izenpe.com +========== +-----BEGIN CERTIFICATE----- +MIIF8TCCA9mgAwIBAgIQALC3WhZIX7/hy/WL1xnmfTANBgkqhkiG9w0BAQsFADA4MQswCQYDVQQG +EwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6ZW5wZS5jb20wHhcNMDcxMjEz +MTMwODI4WhcNMzcxMjEzMDgyNzI1WjA4MQswCQYDVQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMu +QS4xEzARBgNVBAMMCkl6ZW5wZS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ +03rKDx6sp4boFmVqscIbRTJxldn+EFvMr+eleQGPicPK8lVx93e+d5TzcqQsRNiekpsUOqHnJJAK +ClaOxdgmlOHZSOEtPtoKct2jmRXagaKH9HtuJneJWK3W6wyyQXpzbm3benhB6QiIEn6HLmYRY2xU ++zydcsC8Lv/Ct90NduM61/e0aL6i9eOBbsFGb12N4E3GVFWJGjMxCrFXuaOKmMPsOzTFlUFpfnXC +PCDFYbpRR6AgkJOhkEvzTnyFRVSa0QUmQbC1TR0zvsQDyCV8wXDbO/QJLVQnSKwv4cSsPsjLkkxT +OTcj7NMB+eAJRE1NZMDhDVqHIrytG6P+JrUV86f8hBnp7KGItERphIPzidF0BqnMC9bC3ieFUCbK +F7jJeodWLBoBHmy+E60QrLUk9TiRodZL2vG70t5HtfG8gfZZa88ZU+mNFctKy6lvROUbQc/hhqfK +0GqfvEyNBjNaooXlkDWgYlwWTvDjovoDGrQscbNYLN57C9saD+veIR8GdwYDsMnvmfzAuU8Lhij+ +0rnq49qlw0dpEuDb8PYZi+17cNcC1u2HGCgsBCRMd+RIihrGO5rUD8r6ddIBQFqNeb+Lz0vPqhbB +leStTIo+F5HUsWLlguWABKQDfo2/2n+iD5dPDNMN+9fR5XJ+HMh3/1uaD7euBUbl8agW7EekFwID +AQABo4H2MIHzMIGwBgNVHREEgagwgaWBD2luZm9AaXplbnBlLmNvbaSBkTCBjjFHMEUGA1UECgw+ +SVpFTlBFIFMuQS4gLSBDSUYgQTAxMzM3MjYwLVJNZXJjLlZpdG9yaWEtR2FzdGVpeiBUMTA1NSBG +NjIgUzgxQzBBBgNVBAkMOkF2ZGEgZGVsIE1lZGl0ZXJyYW5lbyBFdG9yYmlkZWEgMTQgLSAwMTAx +MCBWaXRvcmlhLUdhc3RlaXowDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0O +BBYEFB0cZQ6o8iV7tJHP5LGx5r1VdGwFMA0GCSqGSIb3DQEBCwUAA4ICAQB4pgwWSp9MiDrAyw6l +Fn2fuUhfGI8NYjb2zRlrrKvV9pF9rnHzP7MOeIWblaQnIUdCSnxIOvVFfLMMjlF4rJUT3sb9fbga +kEyrkgPH7UIBzg/YsfqikuFgba56awmqxinuaElnMIAkejEWOVt+8Rwu3WwJrfIxwYJOubv5vr8q +hT/AQKM6WfxZSzwoJNu0FXWuDYi6LnPAvViH5ULy617uHjAimcs30cQhbIHsvm0m5hzkQiCeR7Cs +g1lwLDXWrzY0tM07+DKo7+N4ifuNRSzanLh+QBxh5z6ikixL8s36mLYp//Pye6kfLqCTVyvehQP5 +aTfLnnhqBbTFMXiJ7HqnheG5ezzevh55hM6fcA5ZwjUukCox2eRFekGkLhObNA5me0mrZJfQRsN5 +nXJQY6aYWwa9SG3YOYNw6DXwBdGqvOPbyALqfP2C2sJbUjWumDqtujWTI6cfSN01RpiyEGjkpTHC +ClguGYEQyVB1/OpaFs4R1+7vUIgtYf8/QnMFlEPVjjxOAToZpR9GTnfQXeWBIiGH/pR9hNiTrdZo +Q0iy2+tzJOeRf1SktoA+naM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1Z +WrOZyGlsQyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw== +-----END CERTIFICATE----- + +Chambers of Commerce Root - 2008 +================================ +-----BEGIN CERTIFICATE----- +MIIHTzCCBTegAwIBAgIJAKPaQn6ksa7aMA0GCSqGSIb3DQEBBQUAMIGuMQswCQYDVQQGEwJFVTFD +MEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNv +bS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMu +QS4xKTAnBgNVBAMTIENoYW1iZXJzIG9mIENvbW1lcmNlIFJvb3QgLSAyMDA4MB4XDTA4MDgwMTEy +Mjk1MFoXDTM4MDczMTEyMjk1MFowga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNl +ZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29tL2FkZHJlc3MpMRIwEAYDVQQF +EwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVyZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJl +cnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC +AQCvAMtwNyuAWko6bHiUfaN/Gh/2NdW928sNRHI+JrKQUrpjOyhYb6WzbZSm891kDFX29ufyIiKA +XuFixrYp4YFs8r/lfTJqVKAyGVn+H4vXPWCGhSRv4xGzdz4gljUha7MI2XAuZPeEklPWDrCQiorj +h40G072QDuKZoRuGDtqaCrsLYVAGUvGef3bsyw/QHg3PmTA9HMRFEFis1tPo1+XqxQEHd9ZR5gN/ +ikilTWh1uem8nk4ZcfUyS5xtYBkL+8ydddy/Js2Pk3g5eXNeJQ7KXOt3EgfLZEFHcpOrUMPrCXZk +NNI5t3YRCQ12RcSprj1qr7V9ZS+UWBDsXHyvfuK2GNnQm05aSd+pZgvMPMZ4fKecHePOjlO+Bd5g +D2vlGts/4+EhySnB8esHnFIbAURRPHsl18TlUlRdJQfKFiC4reRB7noI/plvg6aRArBsNlVq5331 +lubKgdaX8ZSD6e2wsWsSaR6s+12pxZjptFtYer49okQ6Y1nUCyXeG0+95QGezdIp1Z8XGQpvvwyQ +0wlf2eOKNcx5Wk0ZN5K3xMGtr/R5JJqyAQuxr1yW84Ay+1w9mPGgP0revq+ULtlVmhduYJ1jbLhj +ya6BXBg14JC7vjxPNyK5fuvPnnchpj04gftI2jE9K+OJ9dC1vX7gUMQSibMjmhAxhduub+84Mxh2 +EQIDAQABo4IBbDCCAWgwEgYDVR0TAQH/BAgwBgEB/wIBDDAdBgNVHQ4EFgQU+SSsD7K1+HnA+mCI +G8TZTQKeFxkwgeMGA1UdIwSB2zCB2IAU+SSsD7K1+HnA+mCIG8TZTQKeFxmhgbSkgbEwga4xCzAJ +BgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNlZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNh +bWVyZmlybWEuY29tL2FkZHJlc3MpMRIwEAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENh +bWVyZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDiC +CQCj2kJ+pLGu2jAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUH +AgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZIhvcNAQEFBQADggIBAJASryI1 +wqM58C7e6bXpeHxIvj99RZJe6dqxGfwWPJ+0W2aeaufDuV2I6A+tzyMP3iU6XsxPpcG1Lawk0lgH +3qLPaYRgM+gQDROpI9CF5Y57pp49chNyM/WqfcZjHwj0/gF/JM8rLFQJ3uIrbZLGOU8W6jx+ekbU +RWpGqOt1glanq6B8aBMz9p0w8G8nOSQjKpD9kCk18pPfNKXG9/jvjA9iSnyu0/VU+I22mlaHFoI6 +M6taIgj3grrqLuBHmrS1RaMFO9ncLkVAO+rcf+g769HsJtg1pDDFOqxXnrN2pSB7+R5KBWIBpih1 +YJeSDW4+TTdDDZIVnBgizVGZoCkaPF+KMjNbMMeJL0eYD6MDxvbxrN8y8NmBGuScvfaAFPDRLLmF +9dijscilIeUcE5fuDr3fKanvNFNb0+RqE4QGtjICxFKuItLcsiFCGtpA8CnJ7AoMXOLQusxI0zcK +zBIKinmwPQN/aUv0NCB9szTqjktk9T79syNnFQ0EuPAtwQlRPLJsFfClI9eDdOTlLsn+mCdCxqvG +nrDQWzilm1DefhiYtUU79nm06PcaewaD+9CL2rvHvRirCG88gGtAPxkZumWK5r7VXNM21+9AUiRg +OGcEMeyP84LG3rlV8zsxkVrctQgVrXYlCg17LofiDKYGvCYQbTed7N14jHyAxfDZd0jQ +-----END CERTIFICATE----- + +Global Chambersign Root - 2008 +============================== +-----BEGIN CERTIFICATE----- +MIIHSTCCBTGgAwIBAgIJAMnN0+nVfSPOMA0GCSqGSIb3DQEBBQUAMIGsMQswCQYDVQQGEwJFVTFD +MEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNv +bS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMu +QS4xJzAlBgNVBAMTHkdsb2JhbCBDaGFtYmVyc2lnbiBSb290IC0gMjAwODAeFw0wODA4MDExMjMx +NDBaFw0zODA3MzExMjMxNDBaMIGsMQswCQYDVQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUg +Y3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJ +QTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAlBgNVBAMTHkdsb2JhbCBD +aGFtYmVyc2lnbiBSb290IC0gMjAwODCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMDf +VtPkOpt2RbQT2//BthmLN0EYlVJH6xedKYiONWwGMi5HYvNJBL99RDaxccy9Wglz1dmFRP+RVyXf +XjaOcNFccUMd2drvXNL7G706tcuto8xEpw2uIRU/uXpbknXYpBI4iRmKt4DS4jJvVpyR1ogQC7N0 +ZJJ0YPP2zxhPYLIj0Mc7zmFLmY/CDNBAspjcDahOo7kKrmCgrUVSY7pmvWjg+b4aqIG7HkF4ddPB +/gBVsIdU6CeQNR1MM62X/JcumIS/LMmjv9GYERTtY/jKmIhYF5ntRQOXfjyGHoiMvvKRhI9lNNgA +TH23MRdaKXoKGCQwoze1eqkBfSbW+Q6OWfH9GzO1KTsXO0G2Id3UwD2ln58fQ1DJu7xsepeY7s2M +H/ucUa6LcL0nn3HAa6x9kGbo1106DbDVwo3VyJ2dwW3Q0L9R5OP4wzg2rtandeavhENdk5IMagfe +Ox2YItaswTXbo6Al/3K1dh3ebeksZixShNBFks4c5eUzHdwHU1SjqoI7mjcv3N2gZOnm3b2u/GSF +HTynyQbehP9r6GsaPMWis0L7iwk+XwhSx2LE1AVxv8Rk5Pihg+g+EpuoHtQ2TS9x9o0o9oOpE9Jh +wZG7SMA0j0GMS0zbaRL/UJScIINZc+18ofLx/d33SdNDWKBWY8o9PeU1VlnpDsogzCtLkykPAgMB +AAGjggFqMIIBZjASBgNVHRMBAf8ECDAGAQH/AgEMMB0GA1UdDgQWBBS5CcqcHtvTbDprru1U8VuT +BjUuXjCB4QYDVR0jBIHZMIHWgBS5CcqcHtvTbDprru1U8VuTBjUuXqGBsqSBrzCBrDELMAkGA1UE +BhMCRVUxQzBBBgNVBAcTOk1hZHJpZCAoc2VlIGN1cnJlbnQgYWRkcmVzcyBhdCB3d3cuY2FtZXJm +aXJtYS5jb20vYWRkcmVzcykxEjAQBgNVBAUTCUE4Mjc0MzI4NzEbMBkGA1UEChMSQUMgQ2FtZXJm +aXJtYSBTLkEuMScwJQYDVQQDEx5HbG9iYWwgQ2hhbWJlcnNpZ24gUm9vdCAtIDIwMDiCCQDJzdPp +1X0jzjAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUHAgEWHGh0 +dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZIhvcNAQEFBQADggIBAICIf3DekijZBZRG +/5BXqfEv3xoNa/p8DhxJJHkn2EaqbylZUohwEurdPfWbU1Rv4WCiqAm57OtZfMY18dwY6fFn5a+6 +ReAJ3spED8IXDneRRXozX1+WLGiLwUePmJs9wOzL9dWCkoQ10b42OFZyMVtHLaoXpGNR6woBrX/s +dZ7LoR/xfxKxueRkf2fWIyr0uDldmOghp+G9PUIadJpwr2hsUF1Jz//7Dl3mLEfXgTpZALVza2Mg +9jFFCDkO9HB+QHBaP9BrQql0PSgvAm11cpUJjUhjxsYjV5KTXjXBjfkK9yydYhz2rXzdpjEetrHH +foUm+qRqtdpjMNHvkzeyZi99Bffnt0uYlDXA2TopwZ2yUDMdSqlapskD7+3056huirRXhOukP9Du +qqqHW2Pok+JrqNS4cnhrG+055F3Lm6qH1U9OAP7Zap88MQ8oAgF9mOinsKJknnn4SPIVqczmyETr +P3iZ8ntxPjzxmKfFGBI/5rsoM0LpRQp8bfKGeS/Fghl9CYl8slR2iK7ewfPM4W7bMdaTrpmg7yVq +c5iJWzouE4gev8CSlDQb4ye3ix5vQv/n6TebUB0tovkC7stYWDpxvGjjqsGvHCgfotwjZT+B6q6Z +09gwzxMNTxXJhLynSC34MCN32EZLeW32jO06f2ARePTpm67VVMB0gNELQp/B +-----END CERTIFICATE----- + +Go Daddy Root Certificate Authority - G2 +======================================== +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT +B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoTEUdvRGFkZHkuY29tLCBJbmMu +MTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5 +MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 +b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8G +A1UEAxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKDE6bFIEMBO4Tx5oVJnyfq +9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD ++qK+ihVqf94Lw7YZFAXK6sOoBJQ7RnwyDfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutd +fMh8+7ArU6SSYmlRJQVhGkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMl +NAJWJwGRtDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEAAaNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFDqahQcQZyi27/a9 +BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmXWWcDYfF+OwYxdS2hII5PZYe096ac +vNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r +5N9ss4UXnT3ZJE95kTXWXwTrgIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYV +N8Gb5DKj7Tjo2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO +LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI4uJEvlz36hz1 +-----END CERTIFICATE----- + +Starfield Root Certificate Authority - G2 +========================================= +-----BEGIN CERTIFICATE----- +MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT +B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9s +b2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVsZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0 +eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAw +DgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQg +VGVjaG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZpY2F0ZSBB +dXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL3twQP89o/8ArFv +W59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMgnLRJdzIpVv257IzdIvpy3Cdhl+72WoTs +bhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNk +N3mSwOxGXn/hbVNMYq/NHwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7Nf +ZTD4p7dNdloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0HZbU +JtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0GCSqGSIb3DQEBCwUAA4IBAQARWfol +TwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjUsHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx +4mcujJUDJi5DnUox9g61DLu34jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUw +F5okxBDgBPfg8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K +pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1mMpYjn0q7pBZ +c2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0 +-----END CERTIFICATE----- + +Starfield Services Root Certificate Authority - G2 +================================================== +-----BEGIN CERTIFICATE----- +MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMxEDAOBgNVBAgT +B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9s +b2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVsZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRl +IEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNV +BAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxT +dGFyZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2VydmljZXMg +Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20pOsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2 +h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm28xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4Pa +hHQUw2eeBGg6345AWh1KTs9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLP +LJGmpufehRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk6mFB +rMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAwDwYDVR0TAQH/BAUw +AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+qAdcwKziIorhtSpzyEZGDMA0GCSqG +SIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMIbw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPP +E95Dz+I0swSdHynVv/heyNXBve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTy +xQGjhdByPq1zqwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd +iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn0q23KXB56jza +YyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCNsSi6 +-----END CERTIFICATE----- + +AffirmTrust Commercial +====================== +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UEBhMCVVMxFDAS +BgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMB4XDTEw +MDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly +bVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6Eqdb +DuKPHx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yrba0F8PrV +C8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPALMeIrJmqbTFeurCA+ukV6 +BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1yHp52UKqK39c/s4mT6NmgTWvRLpUHhww +MmWd5jyTXlBOeuM61G7MGvv50jeuJCqrVwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNV +HQ4EFgQUnZPGU4teyq8/nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwDQYJKoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYGXUPG +hi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNjvbz4YYCanrHOQnDi +qX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivtZ8SOyUOyXGsViQK8YvxO8rUzqrJv +0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9gN53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0kh +sUlHRUe072o0EclNmsxZt9YCnlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8= +-----END CERTIFICATE----- + +AffirmTrust Networking +====================== +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UEBhMCVVMxFDAS +BgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMB4XDTEw +MDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly +bVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SE +Hi3yYJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbuakCNrmreI +dIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRLQESxG9fhwoXA3hA/Pe24 +/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gb +h+0t+nvujArjqWaJGctB+d1ENmHP4ndGyH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNV +HQ4EFgQUBx/S55zawm6iQLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwDQYJKoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfOtDIu +UFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzuQY0x2+c06lkh1QF6 +12S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZLgo/bNjR9eUJtGxUAArgFU2HdW23 +WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4uolu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9 +/ZFvgrG+CJPbFEfxojfHRZ48x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s= +-----END CERTIFICATE----- + +AffirmTrust Premium +=================== +-----BEGIN CERTIFICATE----- +MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UEBhMCVVMxFDAS +BgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMB4XDTEwMDEy +OTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRy +dXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A +MIICCgKCAgEAxBLfqV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtn +BKAQJG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ+jjeRFcV +5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrSs8PhaJyJ+HoAVt70VZVs ++7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmd +GPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d770O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5R +p9EixAqnOEhss/n/fauGV+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NI +S+LI+H+SqHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S5u04 +6uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4IaC1nEWTJ3s7xgaVY5 +/bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TXOwF0lkLgAOIua+rF7nKsu7/+6qqo ++Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYEFJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB +/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByv +MiPIs0laUZx2KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg +Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B8OWycvpEgjNC +6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQMKSOyARiqcTtNd56l+0OOF6S +L5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK ++4w1IX2COPKpVJEZNZOUbWo6xbLQu4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmV +BtWVyuEklut89pMFu+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFg +IxpHYoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8GKa1qF60 +g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaORtGdFNrHF+QFlozEJLUb +zxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6eKeC2uAloGRwYQw== +-----END CERTIFICATE----- + +AffirmTrust Premium ECC +======================= +-----BEGIN CERTIFICATE----- +MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMCVVMxFDASBgNV +BAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQcmVtaXVtIEVDQzAeFw0xMDAx +MjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1U +cnVzdDEgMB4GA1UEAwwXQWZmaXJtVHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQA +IgNiAAQNMF4bFZ0D0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQ +N8O9ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0GA1UdDgQW +BBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAK +BggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/VsaobgxCd05DhT1wV/GzTjxi+zygk8N53X +57hG8f2h4nECMEJZh0PUUd+60wkyWs6Iflc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKM +eQ== +-----END CERTIFICATE----- + +Certum Trusted Network CA +========================= +-----BEGIN CERTIFICATE----- +MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBMMSIwIAYDVQQK +ExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBUcnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIy +MTIwNzM3WhcNMjkxMjMxMTIwNzM3WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBU +ZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +MSIwIAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rHUV+rpDKmYYe2bg+G0jAC +l/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LMTXPb865Px1bVWqeWifrzq2jUI4ZZJ88J +J7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVUBBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4 +fOQtf/WsX+sWn7Et0brMkUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0 +cvW0QM8xAcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNVHRMB +Af8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNVHQ8BAf8EBAMCAQYw +DQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15ysHhE49wcrwn9I0j6vSrEuVUEtRCj +jSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfLI9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1 +mS1FhIrlQgnXdAIv94nYmem8J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5aj +Zt3hrvJBW8qYVoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI +03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw= +-----END CERTIFICATE----- + +Certinomis - Autorité Racine +============================= +-----BEGIN CERTIFICATE----- +MIIFnDCCA4SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJGUjETMBEGA1UEChMK +Q2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxJjAkBgNVBAMMHUNlcnRpbm9taXMg +LSBBdXRvcml0w6kgUmFjaW5lMB4XDTA4MDkxNzA4Mjg1OVoXDTI4MDkxNzA4Mjg1OVowYzELMAkG +A1UEBhMCRlIxEzARBgNVBAoTCkNlcnRpbm9taXMxFzAVBgNVBAsTDjAwMDIgNDMzOTk4OTAzMSYw +JAYDVQQDDB1DZXJ0aW5vbWlzIC0gQXV0b3JpdMOpIFJhY2luZTCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBAJ2Fn4bT46/HsmtuM+Cet0I0VZ35gb5j2CN2DpdUzZlMGvE5x4jYF1AMnmHa +wE5V3udauHpOd4cN5bjr+p5eex7Ezyh0x5P1FMYiKAT5kcOrJ3NqDi5N8y4oH3DfVS9O7cdxbwly +Lu3VMpfQ8Vh30WC8Tl7bmoT2R2FFK/ZQpn9qcSdIhDWerP5pqZ56XjUl+rSnSTV3lqc2W+HN3yNw +2F1MpQiD8aYkOBOo7C+ooWfHpi2GR+6K/OybDnT0K0kCe5B1jPyZOQE51kqJ5Z52qz6WKDgmi92N +jMD2AR5vpTESOH2VwnHu7XSu5DaiQ3XV8QCb4uTXzEIDS3h65X27uK4uIJPT5GHfceF2Z5c/tt9q +c1pkIuVC28+BA5PY9OMQ4HL2AHCs8MF6DwV/zzRpRbWT5BnbUhYjBYkOjUjkJW+zeL9i9Qf6lSTC +lrLooyPCXQP8w9PlfMl1I9f09bze5N/NgL+RiH2nE7Q5uiy6vdFrzPOlKO1Enn1So2+WLhl+HPNb +xxaOu2B9d2ZHVIIAEWBsMsGoOBvrbpgT1u449fCfDu/+MYHB0iSVL1N6aaLwD4ZFjliCK0wi1F6g +530mJ0jfJUaNSih8hp75mxpZuWW/Bd22Ql095gBIgl4g9xGC3srYn+Y3RyYe63j3YcNBZFgCQfna +4NH4+ej9Uji29YnfAgMBAAGjWzBZMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G +A1UdDgQWBBQNjLZh2kS40RR9w759XkjwzspqsDAXBgNVHSAEEDAOMAwGCiqBegFWAgIAAQEwDQYJ +KoZIhvcNAQEFBQADggIBACQ+YAZ+He86PtvqrxyaLAEL9MW12Ukx9F1BjYkMTv9sov3/4gbIOZ/x +WqndIlgVqIrTseYyCYIDbNc/CMf4uboAbbnW/FIyXaR/pDGUu7ZMOH8oMDX/nyNTt7buFHAAQCva +R6s0fl6nVjBhK4tDrP22iCj1a7Y+YEq6QpA0Z43q619FVDsXrIvkxmUP7tCMXWY5zjKn2BCXwH40 +nJ+U8/aGH88bc62UeYdocMMzpXDn2NU4lG9jeeu/Cg4I58UvD0KgKxRA/yHgBcUn4YQRE7rWhh1B +CxMjidPJC+iKunqjo3M3NYB9Ergzd0A4wPpeMNLytqOx1qKVl4GbUu1pTP+A5FPbVFsDbVRfsbjv +JL1vnxHDx2TCDyhihWZeGnuyt++uNckZM6i4J9szVb9o4XVIRFb7zdNIu0eJOqxp9YDG5ERQL1TE +qkPFMTFYvZbF6nVsmnWxTfj3l/+WFvKXTej28xH5On2KOG4Ey+HTRRWqpdEdnV1j6CTmNhTih60b +WfVEm/vXd3wfAXBioSAaosUaKPQhA+4u2cGA6rnZgtZbdsLLO7XSAPCjDuGtbkD326C00EauFddE +wk01+dIL8hf2rGbVJLJP0RyZwG71fet0BLj5TXcJ17TPBzAJ8bgAVtkXFhYKK4bfjwEZGuW7gmP/ +vgt2Fl43N+bYdJeimUV5 +-----END CERTIFICATE----- + +Root CA Generalitat Valenciana +============================== +-----BEGIN CERTIFICATE----- +MIIGizCCBXOgAwIBAgIEO0XlaDANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJFUzEfMB0GA1UE +ChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UECxMGUEtJR1ZBMScwJQYDVQQDEx5Sb290 +IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmEwHhcNMDEwNzA2MTYyMjQ3WhcNMjEwNzAxMTUyMjQ3 +WjBoMQswCQYDVQQGEwJFUzEfMB0GA1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UE +CxMGUEtJR1ZBMScwJQYDVQQDEx5Sb290IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmEwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGKqtXETcvIorKA3Qdyu0togu8M1JAJke+WmmmO3I2 +F0zo37i7L3bhQEZ0ZQKQUgi0/6iMweDHiVYQOTPvaLRfX9ptI6GJXiKjSgbwJ/BXufjpTjJ3Cj9B +ZPPrZe52/lSqfR0grvPXdMIKX/UIKFIIzFVd0g/bmoGlu6GzwZTNVOAydTGRGmKy3nXiz0+J2ZGQ +D0EbtFpKd71ng+CT516nDOeB0/RSrFOyA8dEJvt55cs0YFAQexvba9dHq198aMpunUEDEO5rmXte +JajCq+TA81yc477OMUxkHl6AovWDfgzWyoxVjr7gvkkHD6MkQXpYHYTqWBLI4bft75PelAgxAgMB +AAGjggM7MIIDNzAyBggrBgEFBQcBAQQmMCQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9vY3NwLnBraS5n +dmEuZXMwEgYDVR0TAQH/BAgwBgEB/wIBAjCCAjQGA1UdIASCAiswggInMIICIwYKKwYBBAG/VQIB +ADCCAhMwggHoBggrBgEFBQcCAjCCAdoeggHWAEEAdQB0AG8AcgBpAGQAYQBkACAAZABlACAAQwBl +AHIAdABpAGYAaQBjAGEAYwBpAPMAbgAgAFIAYQDtAHoAIABkAGUAIABsAGEAIABHAGUAbgBlAHIA +YQBsAGkAdABhAHQAIABWAGEAbABlAG4AYwBpAGEAbgBhAC4ADQAKAEwAYQAgAEQAZQBjAGwAYQBy +AGEAYwBpAPMAbgAgAGQAZQAgAFAAcgDhAGMAdABpAGMAYQBzACAAZABlACAAQwBlAHIAdABpAGYA +aQBjAGEAYwBpAPMAbgAgAHEAdQBlACAAcgBpAGcAZQAgAGUAbAAgAGYAdQBuAGMAaQBvAG4AYQBt +AGkAZQBuAHQAbwAgAGQAZQAgAGwAYQAgAHAAcgBlAHMAZQBuAHQAZQAgAEEAdQB0AG8AcgBpAGQA +YQBkACAAZABlACAAQwBlAHIAdABpAGYAaQBjAGEAYwBpAPMAbgAgAHMAZQAgAGUAbgBjAHUAZQBu +AHQAcgBhACAAZQBuACAAbABhACAAZABpAHIAZQBjAGMAaQDzAG4AIAB3AGUAYgAgAGgAdAB0AHAA +OgAvAC8AdwB3AHcALgBwAGsAaQAuAGcAdgBhAC4AZQBzAC8AYwBwAHMwJQYIKwYBBQUHAgEWGWh0 +dHA6Ly93d3cucGtpLmd2YS5lcy9jcHMwHQYDVR0OBBYEFHs100DSHHgZZu90ECjcPk+yeAT8MIGV +BgNVHSMEgY0wgYqAFHs100DSHHgZZu90ECjcPk+yeAT8oWykajBoMQswCQYDVQQGEwJFUzEfMB0G +A1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UECxMGUEtJR1ZBMScwJQYDVQQDEx5S +b290IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmGCBDtF5WgwDQYJKoZIhvcNAQEFBQADggEBACRh +TvW1yEICKrNcda3FbcrnlD+laJWIwVTAEGmiEi8YPyVQqHxK6sYJ2fR1xkDar1CdPaUWu20xxsdz +Ckj+IHLtb8zog2EWRpABlUt9jppSCS/2bxzkoXHPjCpaF3ODR00PNvsETUlR4hTJZGH71BTg9J63 +NI8KJr2XXPR5OkowGcytT6CYirQxlyric21+eLj4iIlPsSKRZEv1UN4D2+XFducTZnV+ZfsBn5OH +iJ35Rld8TWCvmHMTI6QgkYH60GFmuH3Rr9ZvHmw96RH9qfmCIoaZM3Fa6hlXPZHNqcCjbgcTpsnt ++GijnsNacgmHKNHEc8RzGF9QdRYxn7fofMM= +-----END CERTIFICATE----- + +A-Trust-nQual-03 +================ +-----BEGIN CERTIFICATE----- +MIIDzzCCAregAwIBAgIDAWweMA0GCSqGSIb3DQEBBQUAMIGNMQswCQYDVQQGEwJBVDFIMEYGA1UE +Cgw/QS1UcnVzdCBHZXMuIGYuIFNpY2hlcmhlaXRzc3lzdGVtZSBpbSBlbGVrdHIuIERhdGVudmVy +a2VociBHbWJIMRkwFwYDVQQLDBBBLVRydXN0LW5RdWFsLTAzMRkwFwYDVQQDDBBBLVRydXN0LW5R +dWFsLTAzMB4XDTA1MDgxNzIyMDAwMFoXDTE1MDgxNzIyMDAwMFowgY0xCzAJBgNVBAYTAkFUMUgw +RgYDVQQKDD9BLVRydXN0IEdlcy4gZi4gU2ljaGVyaGVpdHNzeXN0ZW1lIGltIGVsZWt0ci4gRGF0 +ZW52ZXJrZWhyIEdtYkgxGTAXBgNVBAsMEEEtVHJ1c3QtblF1YWwtMDMxGTAXBgNVBAMMEEEtVHJ1 +c3QtblF1YWwtMDMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtPWFuA/OQO8BBC4SA +zewqo51ru27CQoT3URThoKgtUaNR8t4j8DRE/5TrzAUjlUC5B3ilJfYKvUWG6Nm9wASOhURh73+n +yfrBJcyFLGM/BWBzSQXgYHiVEEvc+RFZznF/QJuKqiTfC0Li21a8StKlDJu3Qz7dg9MmEALP6iPE +SU7l0+m0iKsMrmKS1GWH2WrX9IWf5DMiJaXlyDO6w8dB3F/GaswADm0yqLaHNgBid5seHzTLkDx4 +iHQF63n1k3Flyp3HaxgtPVxO59X4PzF9j4fsCiIvI+n+u33J4PTs63zEsMMtYrWacdaxaujs2e3V +cuy+VwHOBVWf3tFgiBCzAgMBAAGjNjA0MA8GA1UdEwEB/wQFMAMBAf8wEQYDVR0OBAoECERqlWdV +eRFPMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAVdRU0VlIXLOThaq/Yy/kgM40 +ozRiPvbY7meIMQQDbwvUB/tOdQ/TLtPAF8fGKOwGDREkDg6lXb+MshOWcdzUzg4NCmgybLlBMRmr +sQd7TZjTXLDR8KdCoLXEjq/+8T/0709GAHbrAvv5ndJAlseIOrifEXnzgGWovR/TeIGgUUw3tKZd +JXDRZslo+S4RFGjxVJgIrCaSD96JntT6s3kr0qN51OyLrIdTaEJMUVF0HhsnLuP1Hyl0Te2v9+GS +mYHovjrHF1D2t8b8m7CKa9aIA5GPBnc6hQLdmNVDeD/GMBWsm2vLV7eJUYs66MmEDNuxUCAKGkq6 +ahq97BvIxYSazQ== +-----END CERTIFICATE----- + +TWCA Root Certification Authority +================================= +-----BEGIN CERTIFICATE----- +MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzESMBAGA1UECgwJ +VEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMzWhcNMzAxMjMxMTU1OTU5WjBfMQswCQYDVQQG +EwJUVzESMBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NB +IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bihSX0NXIP+FPQQeFEAcK0HMMx +QhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQsIBct+HHK3XLfJ+utdGdIzdjp9xC +oi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeXRfwZVzsrb+RH9JlF/h3x+JejiB03HFyP +4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/zrX2SYgJbKdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1r +y+UPizgN7gr8/g+YnzAx3WxSZfmLgb4i4RxYA7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIB +BjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkqhkiG +9w0BAQUFAAOCAQEAPNV3PdrfibqHDAhUaiBQkr6wQT25JmSDCi/oQMCXKCeCMErJk/9q56YAf4lC +mtYR5VPOL8zy2gXE/uJQxDqGfczafhAJO5I1KlOy/usrBdlsXebQ79NqZp4VKIV66IIArB6nCWlW +QtNoURi+VJq/REG6Sb4gumlc7rh3zc5sH62Dlhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVY +T0bf+215WfKEIlKuD8z7fDvnaspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocny +Yh0igzyXxfkZYiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw== +-----END CERTIFICATE----- + +Security Communication RootCA2 +============================== +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDElMCMGA1UEChMc +U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMeU2VjdXJpdHkgQ29tbXVuaWNh +dGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoXDTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMC +SlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3Vy +aXR5IENvbW11bmljYXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +ANAVOVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGrzbl+dp++ ++T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVMVAX3NuRFg3sUZdbcDE3R +3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQhNBqyjoGADdH5H5XTz+L62e4iKrFvlNV +spHEfbmwhRkGeC7bYRr6hfVKkaHnFtWOojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1K +EOtOghY6rCcMU/Gt1SSwawNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8 +QIH4D5csOPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEB +CwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpFcoJxDjrSzG+ntKEj +u/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXcokgfGT+Ok+vx+hfuzU7jBBJV1uXk +3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6q +tnRGEmyR7jTV7JqR50S+kDFy1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29 +mvVXIwAHIRc/SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03 +-----END CERTIFICATE----- + +EC-ACC +====== +-----BEGIN CERTIFICATE----- +MIIFVjCCBD6gAwIBAgIQ7is969Qh3hSoYqwE893EATANBgkqhkiG9w0BAQUFADCB8zELMAkGA1UE +BhMCRVMxOzA5BgNVBAoTMkFnZW5jaWEgQ2F0YWxhbmEgZGUgQ2VydGlmaWNhY2lvIChOSUYgUS0w +ODAxMTc2LUkpMSgwJgYDVQQLEx9TZXJ2ZWlzIFB1YmxpY3MgZGUgQ2VydGlmaWNhY2lvMTUwMwYD +VQQLEyxWZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5ldC92ZXJhcnJlbCAoYykwMzE1MDMGA1UE +CxMsSmVyYXJxdWlhIEVudGl0YXRzIGRlIENlcnRpZmljYWNpbyBDYXRhbGFuZXMxDzANBgNVBAMT +BkVDLUFDQzAeFw0wMzAxMDcyMzAwMDBaFw0zMTAxMDcyMjU5NTlaMIHzMQswCQYDVQQGEwJFUzE7 +MDkGA1UEChMyQWdlbmNpYSBDYXRhbGFuYSBkZSBDZXJ0aWZpY2FjaW8gKE5JRiBRLTA4MDExNzYt +SSkxKDAmBgNVBAsTH1NlcnZlaXMgUHVibGljcyBkZSBDZXJ0aWZpY2FjaW8xNTAzBgNVBAsTLFZl +Z2V1IGh0dHBzOi8vd3d3LmNhdGNlcnQubmV0L3ZlcmFycmVsIChjKTAzMTUwMwYDVQQLEyxKZXJh +cnF1aWEgRW50aXRhdHMgZGUgQ2VydGlmaWNhY2lvIENhdGFsYW5lczEPMA0GA1UEAxMGRUMtQUND +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsyLHT+KXQpWIR4NA9h0X84NzJB5R85iK +w5K4/0CQBXCHYMkAqbWUZRkiFRfCQ2xmRJoNBD45b6VLeqpjt4pEndljkYRm4CgPukLjbo73FCeT +ae6RDqNfDrHrZqJyTxIThmV6PttPB/SnCWDaOkKZx7J/sxaVHMf5NLWUhdWZXqBIoH7nF2W4onW4 +HvPlQn2v7fOKSGRdghST2MDk/7NQcvJ29rNdQlB50JQ+awwAvthrDk4q7D7SzIKiGGUzE3eeml0a +E9jD2z3Il3rucO2n5nzbcc8tlGLfbdb1OL4/pYUKGbio2Al1QnDE6u/LDsg0qBIimAy4E5S2S+zw +0JDnJwIDAQABo4HjMIHgMB0GA1UdEQQWMBSBEmVjX2FjY0BjYXRjZXJ0Lm5ldDAPBgNVHRMBAf8E +BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUoMOLRKo3pUW/l4Ba0fF4opvpXY0wfwYD +VR0gBHgwdjB0BgsrBgEEAfV4AQMBCjBlMCwGCCsGAQUFBwIBFiBodHRwczovL3d3dy5jYXRjZXJ0 +Lm5ldC92ZXJhcnJlbDA1BggrBgEFBQcCAjApGidWZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5l +dC92ZXJhcnJlbCAwDQYJKoZIhvcNAQEFBQADggEBAKBIW4IB9k1IuDlVNZyAelOZ1Vr/sXE7zDkJ +lF7W2u++AVtd0x7Y/X1PzaBB4DSTv8vihpw3kpBWHNzrKQXlxJ7HNd+KDM3FIUPpqojlNcAZQmNa +Al6kSBg6hW/cnbw/nZzBh7h6YQjpdwt/cKt63dmXLGQehb+8dJahw3oS7AwaboMMPOhyRp/7SNVe +l+axofjk70YllJyJ22k4vuxcDlbHZVHlUIiIv0LVKz3l+bqeLrPK9HOSAgu+TGbrIP65y7WZf+a2 +E/rKS03Z7lNGBjvGTq2TWoF+bCpLagVFjPIhpDGQh2xlnJ2lYJU6Un/10asIbvPuW/mIPX64b24D +5EI= +-----END CERTIFICATE----- + +Hellenic Academic and Research Institutions RootCA 2011 +======================================================= +-----BEGIN CERTIFICATE----- +MIIEMTCCAxmgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBlTELMAkGA1UEBhMCR1IxRDBCBgNVBAoT +O0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9y +aXR5MUAwPgYDVQQDEzdIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25z +IFJvb3RDQSAyMDExMB4XDTExMTIwNjEzNDk1MloXDTMxMTIwMTEzNDk1MlowgZUxCzAJBgNVBAYT +AkdSMUQwQgYDVQQKEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25z +IENlcnQuIEF1dGhvcml0eTFAMD4GA1UEAxM3SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNo +IEluc3RpdHV0aW9ucyBSb290Q0EgMjAxMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +AKlTAOMupvaO+mDYLZU++CwqVE7NuYRhlFhPjz2L5EPzdYmNUeTDN9KKiE15HrcS3UN4SoqS5tdI +1Q+kOilENbgH9mgdVc04UfCMJDGFr4PJfel3r+0ae50X+bOdOFAPplp5kYCvN66m0zH7tSYJnTxa +71HFK9+WXesyHgLacEnsbgzImjeN9/E2YEsmLIKe0HjzDQ9jpFEw4fkrJxIH2Oq9GGKYsFk3fb7u +8yBRQlqD75O6aRXxYp2fmTmCobd0LovUxQt7L/DICto9eQqakxylKHJzkUOap9FNhYS5qXSPFEDH +3N6sQWRstBmbAmNtJGSPRLIl6s5ddAxjMlyNh+UCAwEAAaOBiTCBhjAPBgNVHRMBAf8EBTADAQH/ +MAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQUppFC/RNhSiOeCKQp5dgTBCPuQSUwRwYDVR0eBEAwPqA8 +MAWCAy5ncjAFggMuZXUwBoIELmVkdTAGggQub3JnMAWBAy5ncjAFgQMuZXUwBoEELmVkdTAGgQQu +b3JnMA0GCSqGSIb3DQEBBQUAA4IBAQAf73lB4XtuP7KMhjdCSk4cNx6NZrokgclPEg8hwAOXhiVt +XdMiKahsog2p6z0GW5k6x8zDmjR/qw7IThzh+uTczQ2+vyT+bOdrwg3IBp5OjWEopmr95fZi6hg8 +TqBTnbI6nOulnJEWtk2C4AwFSKls9cz4y51JtPACpf1wA+2KIaWuE4ZJwzNzvoc7dIsXRSZMFpGD +/md9zU1jZ/rzAxKWeAaNsWftjj++n08C9bMJL/NMh98qy5V8AcysNnq/onN694/BtZqhFLKPM58N +7yLcZnuEvUUXBj08yrl3NI/K6s8/MT7jiOOASSXIl7WdmplNsDz4SgCbZN2fOUvRJ9e4 +-----END CERTIFICATE----- diff --git a/3rdparty/aws-sdk/lib/requestcore/requestcore.class.php b/3rdparty/aws-sdk/lib/requestcore/requestcore.class.php new file mode 100755 index 0000000000..087694f08c --- /dev/null +++ b/3rdparty/aws-sdk/lib/requestcore/requestcore.class.php @@ -0,0 +1,1028 @@ +). + */ + public $request_class = 'RequestCore'; + + /** + * The default class to use for HTTP Responses (defaults to ). + */ + public $response_class = 'ResponseCore'; + + /** + * Default useragent string to use. + */ + public $useragent = 'RequestCore/1.4.4'; + + /** + * File to read from while streaming up. + */ + public $read_file = null; + + /** + * The resource to read from while streaming up. + */ + public $read_stream = null; + + /** + * The size of the stream to read from. + */ + public $read_stream_size = null; + + /** + * The length already read from the stream. + */ + public $read_stream_read = 0; + + /** + * File to write to while streaming down. + */ + public $write_file = null; + + /** + * The resource to write to while streaming down. + */ + public $write_stream = null; + + /** + * Stores the intended starting seek position. + */ + public $seek_position = null; + + /** + * The location of the cacert.pem file to use. + */ + public $cacert_location = false; + + /** + * The state of SSL certificate verification. + */ + public $ssl_verification = true; + + /** + * The user-defined callback function to call when a stream is read from. + */ + public $registered_streaming_read_callback = null; + + /** + * The user-defined callback function to call when a stream is written to. + */ + public $registered_streaming_write_callback = null; + + + /*%******************************************************************************************%*/ + // CONSTANTS + + /** + * GET HTTP Method + */ + const HTTP_GET = 'GET'; + + /** + * POST HTTP Method + */ + const HTTP_POST = 'POST'; + + /** + * PUT HTTP Method + */ + const HTTP_PUT = 'PUT'; + + /** + * DELETE HTTP Method + */ + const HTTP_DELETE = 'DELETE'; + + /** + * HEAD HTTP Method + */ + const HTTP_HEAD = 'HEAD'; + + + /*%******************************************************************************************%*/ + // CONSTRUCTOR/DESTRUCTOR + + /** + * Constructs a new instance of this class. + * + * @param string $url (Optional) The URL to request or service endpoint to query. + * @param string $proxy (Optional) The faux-url to use for proxy settings. Takes the following format: `proxy://user:pass@hostname:port` + * @param array $helpers (Optional) An associative array of classnames to use for request, and response functionality. Gets passed in automatically by the calling class. + * @return $this A reference to the current instance. + */ + public function __construct($url = null, $proxy = null, $helpers = null) + { + // Set some default values. + $this->request_url = $url; + $this->method = self::HTTP_GET; + $this->request_headers = array(); + $this->request_body = ''; + + // Set a new Request class if one was set. + if (isset($helpers['request']) && !empty($helpers['request'])) + { + $this->request_class = $helpers['request']; + } + + // Set a new Request class if one was set. + if (isset($helpers['response']) && !empty($helpers['response'])) + { + $this->response_class = $helpers['response']; + } + + if ($proxy) + { + $this->set_proxy($proxy); + } + + return $this; + } + + /** + * Destructs the instance. Closes opened file handles. + * + * @return $this A reference to the current instance. + */ + public function __destruct() + { + if (isset($this->read_file) && isset($this->read_stream)) + { + fclose($this->read_stream); + } + + if (isset($this->write_file) && isset($this->write_stream)) + { + fclose($this->write_stream); + } + + return $this; + } + + + /*%******************************************************************************************%*/ + // REQUEST METHODS + + /** + * Sets the credentials to use for authentication. + * + * @param string $user (Required) The username to authenticate with. + * @param string $pass (Required) The password to authenticate with. + * @return $this A reference to the current instance. + */ + public function set_credentials($user, $pass) + { + $this->username = $user; + $this->password = $pass; + return $this; + } + + /** + * Adds a custom HTTP header to the cURL request. + * + * @param string $key (Required) The custom HTTP header to set. + * @param mixed $value (Required) The value to assign to the custom HTTP header. + * @return $this A reference to the current instance. + */ + public function add_header($key, $value) + { + $this->request_headers[$key] = $value; + return $this; + } + + /** + * Removes an HTTP header from the cURL request. + * + * @param string $key (Required) The custom HTTP header to set. + * @return $this A reference to the current instance. + */ + public function remove_header($key) + { + if (isset($this->request_headers[$key])) + { + unset($this->request_headers[$key]); + } + return $this; + } + + /** + * Set the method type for the request. + * + * @param string $method (Required) One of the following constants: , , , , . + * @return $this A reference to the current instance. + */ + public function set_method($method) + { + $this->method = strtoupper($method); + return $this; + } + + /** + * Sets a custom useragent string for the class. + * + * @param string $ua (Required) The useragent string to use. + * @return $this A reference to the current instance. + */ + public function set_useragent($ua) + { + $this->useragent = $ua; + return $this; + } + + /** + * Set the body to send in the request. + * + * @param string $body (Required) The textual content to send along in the body of the request. + * @return $this A reference to the current instance. + */ + public function set_body($body) + { + $this->request_body = $body; + return $this; + } + + /** + * Set the URL to make the request to. + * + * @param string $url (Required) The URL to make the request to. + * @return $this A reference to the current instance. + */ + public function set_request_url($url) + { + $this->request_url = $url; + return $this; + } + + /** + * Set additional CURLOPT settings. These will merge with the default settings, and override if + * there is a duplicate. + * + * @param array $curlopts (Optional) A set of key-value pairs that set `CURLOPT` options. These will merge with the existing CURLOPTs, and ones passed here will override the defaults. Keys should be the `CURLOPT_*` constants, not strings. + * @return $this A reference to the current instance. + */ + public function set_curlopts($curlopts) + { + $this->curlopts = $curlopts; + return $this; + } + + /** + * Sets the length in bytes to read from the stream while streaming up. + * + * @param integer $size (Required) The length in bytes to read from the stream. + * @return $this A reference to the current instance. + */ + public function set_read_stream_size($size) + { + $this->read_stream_size = $size; + + return $this; + } + + /** + * Sets the resource to read from while streaming up. Reads the stream from its current position until + * EOF or `$size` bytes have been read. If `$size` is not given it will be determined by and + * . + * + * @param resource $resource (Required) The readable resource to read from. + * @param integer $size (Optional) The size of the stream to read. + * @return $this A reference to the current instance. + */ + public function set_read_stream($resource, $size = null) + { + if (!isset($size) || $size < 0) + { + $stats = fstat($resource); + + if ($stats && $stats['size'] >= 0) + { + $position = ftell($resource); + + if ($position !== false && $position >= 0) + { + $size = $stats['size'] - $position; + } + } + } + + $this->read_stream = $resource; + + return $this->set_read_stream_size($size); + } + + /** + * Sets the file to read from while streaming up. + * + * @param string $location (Required) The readable location to read from. + * @return $this A reference to the current instance. + */ + public function set_read_file($location) + { + $this->read_file = $location; + $read_file_handle = fopen($location, 'r'); + + return $this->set_read_stream($read_file_handle); + } + + /** + * Sets the resource to write to while streaming down. + * + * @param resource $resource (Required) The writeable resource to write to. + * @return $this A reference to the current instance. + */ + public function set_write_stream($resource) + { + $this->write_stream = $resource; + + return $this; + } + + /** + * Sets the file to write to while streaming down. + * + * @param string $location (Required) The writeable location to write to. + * @return $this A reference to the current instance. + */ + public function set_write_file($location) + { + $this->write_file = $location; + $write_file_handle = fopen($location, 'w'); + + return $this->set_write_stream($write_file_handle); + } + + /** + * Set the proxy to use for making requests. + * + * @param string $proxy (Required) The faux-url to use for proxy settings. Takes the following format: `proxy://user:pass@hostname:port` + * @return $this A reference to the current instance. + */ + public function set_proxy($proxy) + { + $proxy = parse_url($proxy); + $proxy['user'] = isset($proxy['user']) ? $proxy['user'] : null; + $proxy['pass'] = isset($proxy['pass']) ? $proxy['pass'] : null; + $proxy['port'] = isset($proxy['port']) ? $proxy['port'] : null; + $this->proxy = $proxy; + return $this; + } + + /** + * Set the intended starting seek position. + * + * @param integer $position (Required) The byte-position of the stream to begin reading from. + * @return $this A reference to the current instance. + */ + public function set_seek_position($position) + { + $this->seek_position = isset($position) ? (integer) $position : null; + + return $this; + } + + /** + * Register a callback function to execute whenever a data stream is read from using + * . + * + * The user-defined callback function should accept three arguments: + * + *
    + *
  • $curl_handle - resource - Required - The cURL handle resource that represents the in-progress transfer.
  • + *
  • $file_handle - resource - Required - The file handle resource that represents the file on the local file system.
  • + *
  • $length - integer - Required - The length in kilobytes of the data chunk that was transferred.
  • + *
+ * + * @param string|array|function $callback (Required) The callback function is called by , so you can pass the following values:
    + *
  • The name of a global function to execute, passed as a string.
  • + *
  • A method to execute, passed as array('ClassName', 'MethodName').
  • + *
  • An anonymous function (PHP 5.3+).
+ * @return $this A reference to the current instance. + */ + public function register_streaming_read_callback($callback) + { + $this->registered_streaming_read_callback = $callback; + + return $this; + } + + /** + * Register a callback function to execute whenever a data stream is written to using + * . + * + * The user-defined callback function should accept two arguments: + * + *
    + *
  • $curl_handle - resource - Required - The cURL handle resource that represents the in-progress transfer.
  • + *
  • $length - integer - Required - The length in kilobytes of the data chunk that was transferred.
  • + *
+ * + * @param string|array|function $callback (Required) The callback function is called by , so you can pass the following values:
    + *
  • The name of a global function to execute, passed as a string.
  • + *
  • A method to execute, passed as array('ClassName', 'MethodName').
  • + *
  • An anonymous function (PHP 5.3+).
+ * @return $this A reference to the current instance. + */ + public function register_streaming_write_callback($callback) + { + $this->registered_streaming_write_callback = $callback; + + return $this; + } + + + /*%******************************************************************************************%*/ + // PREPARE, SEND, AND PROCESS REQUEST + + /** + * A callback function that is invoked by cURL for streaming up. + * + * @param resource $curl_handle (Required) The cURL handle for the request. + * @param resource $file_handle (Required) The open file handle resource. + * @param integer $length (Required) The maximum number of bytes to read. + * @return binary Binary data from a stream. + */ + public function streaming_read_callback($curl_handle, $file_handle, $length) + { + // Once we've sent as much as we're supposed to send... + if ($this->read_stream_read >= $this->read_stream_size) + { + // Send EOF + return ''; + } + + // If we're at the beginning of an upload and need to seek... + if ($this->read_stream_read == 0 && isset($this->seek_position) && $this->seek_position !== ftell($this->read_stream)) + { + if (fseek($this->read_stream, $this->seek_position) !== 0) + { + throw new RequestCore_Exception('The stream does not support seeking and is either not at the requested position or the position is unknown.'); + } + } + + $read = fread($this->read_stream, min($this->read_stream_size - $this->read_stream_read, $length)); // Remaining upload data or cURL's requested chunk size + $this->read_stream_read += strlen($read); + + $out = $read === false ? '' : $read; + + // Execute callback function + if ($this->registered_streaming_read_callback) + { + call_user_func($this->registered_streaming_read_callback, $curl_handle, $file_handle, $out); + } + + return $out; + } + + /** + * A callback function that is invoked by cURL for streaming down. + * + * @param resource $curl_handle (Required) The cURL handle for the request. + * @param binary $data (Required) The data to write. + * @return integer The number of bytes written. + */ + public function streaming_write_callback($curl_handle, $data) + { + $length = strlen($data); + $written_total = 0; + $written_last = 0; + + while ($written_total < $length) + { + $written_last = fwrite($this->write_stream, substr($data, $written_total)); + + if ($written_last === false) + { + return $written_total; + } + + $written_total += $written_last; + } + + // Execute callback function + if ($this->registered_streaming_write_callback) + { + call_user_func($this->registered_streaming_write_callback, $curl_handle, $written_total); + } + + return $written_total; + } + + /** + * Prepares and adds the details of the cURL request. This can be passed along to a + * function. + * + * @return resource The handle for the cURL object. + */ + public function prep_request() + { + $curl_handle = curl_init(); + + // Set default options. + curl_setopt($curl_handle, CURLOPT_URL, $this->request_url); + curl_setopt($curl_handle, CURLOPT_FILETIME, true); + curl_setopt($curl_handle, CURLOPT_FRESH_CONNECT, false); + curl_setopt($curl_handle, CURLOPT_CLOSEPOLICY, CURLCLOSEPOLICY_LEAST_RECENTLY_USED); + curl_setopt($curl_handle, CURLOPT_MAXREDIRS, 5); + curl_setopt($curl_handle, CURLOPT_HEADER, true); + curl_setopt($curl_handle, CURLOPT_RETURNTRANSFER, true); + curl_setopt($curl_handle, CURLOPT_TIMEOUT, 5184000); + curl_setopt($curl_handle, CURLOPT_CONNECTTIMEOUT, 120); + curl_setopt($curl_handle, CURLOPT_NOSIGNAL, true); + curl_setopt($curl_handle, CURLOPT_REFERER, $this->request_url); + curl_setopt($curl_handle, CURLOPT_USERAGENT, $this->useragent); + curl_setopt($curl_handle, CURLOPT_READFUNCTION, array($this, 'streaming_read_callback')); + + // Verification of the SSL cert + if ($this->ssl_verification) + { + curl_setopt($curl_handle, CURLOPT_SSL_VERIFYPEER, true); + curl_setopt($curl_handle, CURLOPT_SSL_VERIFYHOST, 2); + } + else + { + curl_setopt($curl_handle, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($curl_handle, CURLOPT_SSL_VERIFYHOST, false); + } + + // chmod the file as 0755 + if ($this->cacert_location === true) + { + curl_setopt($curl_handle, CURLOPT_CAINFO, dirname(__FILE__) . '/cacert.pem'); + } + elseif (is_string($this->cacert_location)) + { + curl_setopt($curl_handle, CURLOPT_CAINFO, $this->cacert_location); + } + + // Debug mode + if ($this->debug_mode) + { + curl_setopt($curl_handle, CURLOPT_VERBOSE, true); + } + + // Handle open_basedir & safe mode + if (!ini_get('safe_mode') && !ini_get('open_basedir')) + { + curl_setopt($curl_handle, CURLOPT_FOLLOWLOCATION, true); + } + + // Enable a proxy connection if requested. + if ($this->proxy) + { + curl_setopt($curl_handle, CURLOPT_HTTPPROXYTUNNEL, true); + + $host = $this->proxy['host']; + $host .= ($this->proxy['port']) ? ':' . $this->proxy['port'] : ''; + curl_setopt($curl_handle, CURLOPT_PROXY, $host); + + if (isset($this->proxy['user']) && isset($this->proxy['pass'])) + { + curl_setopt($curl_handle, CURLOPT_PROXYUSERPWD, $this->proxy['user'] . ':' . $this->proxy['pass']); + } + } + + // Set credentials for HTTP Basic/Digest Authentication. + if ($this->username && $this->password) + { + curl_setopt($curl_handle, CURLOPT_HTTPAUTH, CURLAUTH_ANY); + curl_setopt($curl_handle, CURLOPT_USERPWD, $this->username . ':' . $this->password); + } + + // Handle the encoding if we can. + if (extension_loaded('zlib')) + { + curl_setopt($curl_handle, CURLOPT_ENCODING, 'gzip, deflate'); + } + + // Process custom headers + if (isset($this->request_headers) && count($this->request_headers)) + { + $temp_headers = array(); + + foreach ($this->request_headers as $k => $v) + { + $temp_headers[] = $k . ': ' . $v; + } + + curl_setopt($curl_handle, CURLOPT_HTTPHEADER, $temp_headers); + } + + switch ($this->method) + { + case self::HTTP_PUT: + curl_setopt($curl_handle, CURLOPT_CUSTOMREQUEST, 'PUT'); + if (isset($this->read_stream)) + { + if (!isset($this->read_stream_size) || $this->read_stream_size < 0) + { + throw new RequestCore_Exception('The stream size for the streaming upload cannot be determined.'); + } + + curl_setopt($curl_handle, CURLOPT_INFILESIZE, $this->read_stream_size); + curl_setopt($curl_handle, CURLOPT_UPLOAD, true); + } + else + { + curl_setopt($curl_handle, CURLOPT_POSTFIELDS, $this->request_body); + } + break; + + case self::HTTP_POST: + curl_setopt($curl_handle, CURLOPT_POST, true); + curl_setopt($curl_handle, CURLOPT_POSTFIELDS, $this->request_body); + break; + + case self::HTTP_HEAD: + curl_setopt($curl_handle, CURLOPT_CUSTOMREQUEST, self::HTTP_HEAD); + curl_setopt($curl_handle, CURLOPT_NOBODY, 1); + break; + + default: // Assumed GET + curl_setopt($curl_handle, CURLOPT_CUSTOMREQUEST, $this->method); + if (isset($this->write_stream)) + { + curl_setopt($curl_handle, CURLOPT_WRITEFUNCTION, array($this, 'streaming_write_callback')); + curl_setopt($curl_handle, CURLOPT_HEADER, false); + } + else + { + curl_setopt($curl_handle, CURLOPT_POSTFIELDS, $this->request_body); + } + break; + } + + // Merge in the CURLOPTs + if (isset($this->curlopts) && sizeof($this->curlopts) > 0) + { + foreach ($this->curlopts as $k => $v) + { + curl_setopt($curl_handle, $k, $v); + } + } + + return $curl_handle; + } + + /** + * Take the post-processed cURL data and break it down into useful header/body/info chunks. Uses the + * data stored in the `curl_handle` and `response` properties unless replacement data is passed in via + * parameters. + * + * @param resource $curl_handle (Optional) The reference to the already executed cURL request. + * @param string $response (Optional) The actual response content itself that needs to be parsed. + * @return ResponseCore A object containing a parsed HTTP response. + */ + public function process_response($curl_handle = null, $response = null) + { + // Accept a custom one if it's passed. + if ($curl_handle && $response) + { + $this->curl_handle = $curl_handle; + $this->response = $response; + } + + // As long as this came back as a valid resource... + if (is_resource($this->curl_handle)) + { + // Determine what's what. + $header_size = curl_getinfo($this->curl_handle, CURLINFO_HEADER_SIZE); + $this->response_headers = substr($this->response, 0, $header_size); + $this->response_body = substr($this->response, $header_size); + $this->response_code = curl_getinfo($this->curl_handle, CURLINFO_HTTP_CODE); + $this->response_info = curl_getinfo($this->curl_handle); + + // Parse out the headers + $this->response_headers = explode("\r\n\r\n", trim($this->response_headers)); + $this->response_headers = array_pop($this->response_headers); + $this->response_headers = explode("\r\n", $this->response_headers); + array_shift($this->response_headers); + + // Loop through and split up the headers. + $header_assoc = array(); + foreach ($this->response_headers as $header) + { + $kv = explode(': ', $header); + $header_assoc[strtolower($kv[0])] = $kv[1]; + } + + // Reset the headers to the appropriate property. + $this->response_headers = $header_assoc; + $this->response_headers['_info'] = $this->response_info; + $this->response_headers['_info']['method'] = $this->method; + + if ($curl_handle && $response) + { + return new $this->response_class($this->response_headers, $this->response_body, $this->response_code, $this->curl_handle); + } + } + + // Return false + return false; + } + + /** + * Sends the request, calling necessary utility functions to update built-in properties. + * + * @param boolean $parse (Optional) Whether to parse the response with ResponseCore or not. + * @return string The resulting unparsed data from the request. + */ + public function send_request($parse = false) + { + set_time_limit(0); + + $curl_handle = $this->prep_request(); + $this->response = curl_exec($curl_handle); + + if ($this->response === false) + { + throw new cURL_Exception('cURL resource: ' . (string) $curl_handle . '; cURL error: ' . curl_error($curl_handle) . ' (cURL error code ' . curl_errno($curl_handle) . '). See http://curl.haxx.se/libcurl/c/libcurl-errors.html for an explanation of error codes.'); + } + + $parsed_response = $this->process_response($curl_handle, $this->response); + + curl_close($curl_handle); + + if ($parse) + { + return $parsed_response; + } + + return $this->response; + } + + /** + * Sends the request using , enabling parallel requests. Uses the "rolling" method. + * + * @param array $handles (Required) An indexed array of cURL handles to process simultaneously. + * @param array $opt (Optional) An associative array of parameters that can have the following keys:
    + *
  • callback - string|array - Optional - The string name of a function to pass the response data to. If this is a method, pass an array where the [0] index is the class and the [1] index is the method name.
  • + *
  • limit - integer - Optional - The number of simultaneous requests to make. This can be useful for scaling around slow server responses. Defaults to trusting cURLs judgement as to how many to use.
+ * @return array Post-processed cURL responses. + */ + public function send_multi_request($handles, $opt = null) + { + set_time_limit(0); + + // Skip everything if there are no handles to process. + if (count($handles) === 0) return array(); + + if (!$opt) $opt = array(); + + // Initialize any missing options + $limit = isset($opt['limit']) ? $opt['limit'] : -1; + + // Initialize + $handle_list = $handles; + $http = new $this->request_class(); + $multi_handle = curl_multi_init(); + $handles_post = array(); + $added = count($handles); + $last_handle = null; + $count = 0; + $i = 0; + + // Loop through the cURL handles and add as many as it set by the limit parameter. + while ($i < $added) + { + if ($limit > 0 && $i >= $limit) break; + curl_multi_add_handle($multi_handle, array_shift($handles)); + $i++; + } + + do + { + $active = false; + + // Start executing and wait for a response. + while (($status = curl_multi_exec($multi_handle, $active)) === CURLM_CALL_MULTI_PERFORM) + { + // Start looking for possible responses immediately when we have to add more handles + if (count($handles) > 0) break; + } + + // Figure out which requests finished. + $to_process = array(); + + while ($done = curl_multi_info_read($multi_handle)) + { + // Since curl_errno() isn't reliable for handles that were in multirequests, we check the 'result' of the info read, which contains the curl error number, (listed here http://curl.haxx.se/libcurl/c/libcurl-errors.html ) + if ($done['result'] > 0) + { + throw new cURL_Multi_Exception('cURL resource: ' . (string) $done['handle'] . '; cURL error: ' . curl_error($done['handle']) . ' (cURL error code ' . $done['result'] . '). See http://curl.haxx.se/libcurl/c/libcurl-errors.html for an explanation of error codes.'); + } + + // Because curl_multi_info_read() might return more than one message about a request, we check to see if this request is already in our array of completed requests + elseif (!isset($to_process[(int) $done['handle']])) + { + $to_process[(int) $done['handle']] = $done; + } + } + + // Actually deal with the request + foreach ($to_process as $pkey => $done) + { + $response = $http->process_response($done['handle'], curl_multi_getcontent($done['handle'])); + $key = array_search($done['handle'], $handle_list, true); + $handles_post[$key] = $response; + + if (count($handles) > 0) + { + curl_multi_add_handle($multi_handle, array_shift($handles)); + } + + curl_multi_remove_handle($multi_handle, $done['handle']); + curl_close($done['handle']); + } + } + while ($active || count($handles_post) < $added); + + curl_multi_close($multi_handle); + + ksort($handles_post, SORT_NUMERIC); + return $handles_post; + } + + + /*%******************************************************************************************%*/ + // RESPONSE METHODS + + /** + * Get the HTTP response headers from the request. + * + * @param string $header (Optional) A specific header value to return. Defaults to all headers. + * @return string|array All or selected header values. + */ + public function get_response_header($header = null) + { + if ($header) + { + return $this->response_headers[strtolower($header)]; + } + return $this->response_headers; + } + + /** + * Get the HTTP response body from the request. + * + * @return string The response body. + */ + public function get_response_body() + { + return $this->response_body; + } + + /** + * Get the HTTP response code from the request. + * + * @return string The HTTP response code. + */ + public function get_response_code() + { + return $this->response_code; + } +} + + +/** + * Container for all response-related methods. + */ +class ResponseCore +{ + /** + * Stores the HTTP header information. + */ + public $header; + + /** + * Stores the SimpleXML response. + */ + public $body; + + /** + * Stores the HTTP response code. + */ + public $status; + + /** + * Constructs a new instance of this class. + * + * @param array $header (Required) Associative array of HTTP headers (typically returned by ). + * @param string $body (Required) XML-formatted response from AWS. + * @param integer $status (Optional) HTTP response status code from the request. + * @return object Contains an `header` property (HTTP headers as an associative array), a or `body` property, and an `status` code. + */ + public function __construct($header, $body, $status = null) + { + $this->header = $header; + $this->body = $body; + $this->status = $status; + + return $this; + } + + /** + * Did we receive the status code we expected? + * + * @param integer|array $codes (Optional) The status code(s) to expect. Pass an for a single acceptable value, or an of integers for multiple acceptable values. + * @return boolean Whether we received the expected status code or not. + */ + public function isOK($codes = array(200, 201, 204, 206)) + { + if (is_array($codes)) + { + return in_array($this->status, $codes); + } + + return $this->status === $codes; + } +} + +class cURL_Exception extends Exception {} +class cURL_Multi_Exception extends cURL_Exception {} +class RequestCore_Exception extends Exception {} diff --git a/3rdparty/aws-sdk/lib/yaml/LICENSE b/3rdparty/aws-sdk/lib/yaml/LICENSE new file mode 100644 index 0000000000..3cef853170 --- /dev/null +++ b/3rdparty/aws-sdk/lib/yaml/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2008-2009 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/3rdparty/aws-sdk/lib/yaml/README.markdown b/3rdparty/aws-sdk/lib/yaml/README.markdown new file mode 100644 index 0000000000..e4f80cfbac --- /dev/null +++ b/3rdparty/aws-sdk/lib/yaml/README.markdown @@ -0,0 +1,15 @@ +Symfony YAML: A PHP library that speaks YAML +============================================ + +Symfony YAML is a PHP library that parses YAML strings and converts them to +PHP arrays. It can also converts PHP arrays to YAML strings. Its official +website is at http://components.symfony-project.org/yaml/. + +The documentation is to be found in the `doc/` directory. + +Symfony YAML is licensed under the MIT license (see LICENSE file). + +The Symfony YAML library is developed and maintained by the +[symfony](http://www.symfony-project.org/) project team. It has been extracted +from symfony to be used as a standalone library. Symfony YAML is part of the +[symfony components project](http://components.symfony-project.org/). diff --git a/3rdparty/aws-sdk/lib/yaml/lib/sfYaml.php b/3rdparty/aws-sdk/lib/yaml/lib/sfYaml.php new file mode 100644 index 0000000000..1d89ccc973 --- /dev/null +++ b/3rdparty/aws-sdk/lib/yaml/lib/sfYaml.php @@ -0,0 +1,135 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * sfYaml offers convenience methods to load and dump YAML. + * + * @package symfony + * @subpackage yaml + * @author Fabien Potencier + * @version SVN: $Id: sfYaml.class.php 8988 2008-05-15 20:24:26Z fabien $ + */ +class sfYaml +{ + static protected + $spec = '1.2'; + + /** + * Sets the YAML specification version to use. + * + * @param string $version The YAML specification version + */ + static public function setSpecVersion($version) + { + if (!in_array($version, array('1.1', '1.2'))) + { + throw new InvalidArgumentException(sprintf('Version %s of the YAML specifications is not supported', $version)); + } + + self::$spec = $version; + } + + /** + * Gets the YAML specification version to use. + * + * @return string The YAML specification version + */ + static public function getSpecVersion() + { + return self::$spec; + } + + /** + * Loads YAML into a PHP array. + * + * The load method, when supplied with a YAML stream (string or file), + * will do its best to convert YAML in a file into a PHP array. + * + * Usage: + * + * $array = sfYaml::load('config.yml'); + * print_r($array); + * + * + * @param string $input Path of YAML file or string containing YAML + * + * @return array The YAML converted to a PHP array + * + * @throws InvalidArgumentException If the YAML is not valid + */ + public static function load($input) + { + $file = ''; + + // if input is a file, process it + if (strpos($input, "\n") === false && is_file($input)) + { + $file = $input; + + ob_start(); + $retval = include($input); + $content = ob_get_clean(); + + // if an array is returned by the config file assume it's in plain php form else in YAML + $input = is_array($retval) ? $retval : $content; + } + + // if an array is returned by the config file assume it's in plain php form else in YAML + if (is_array($input)) + { + return $input; + } + + require_once dirname(__FILE__).'/sfYamlParser.php'; + + $yaml = new sfYamlParser(); + + try + { + $ret = $yaml->parse($input); + } + catch (Exception $e) + { + throw new InvalidArgumentException(sprintf('Unable to parse %s: %s', $file ? sprintf('file "%s"', $file) : 'string', $e->getMessage())); + } + + return $ret; + } + + /** + * Dumps a PHP array to a YAML string. + * + * The dump method, when supplied with an array, will do its best + * to convert the array into friendly YAML. + * + * @param array $array PHP array + * @param integer $inline The level where you switch to inline YAML + * + * @return string A YAML string representing the original PHP array + */ + public static function dump($array, $inline = 2) + { + require_once dirname(__FILE__).'/sfYamlDumper.php'; + + $yaml = new sfYamlDumper(); + + return $yaml->dump($array, $inline); + } +} + +/** + * Wraps echo to automatically provide a newline. + * + * @param string $string The string to echo with new line + */ +function echoln($string) +{ + echo $string."\n"; +} diff --git a/3rdparty/aws-sdk/lib/yaml/lib/sfYamlDumper.php b/3rdparty/aws-sdk/lib/yaml/lib/sfYamlDumper.php new file mode 100644 index 0000000000..0ada2b37d2 --- /dev/null +++ b/3rdparty/aws-sdk/lib/yaml/lib/sfYamlDumper.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +require_once(dirname(__FILE__).'/sfYamlInline.php'); + +/** + * sfYamlDumper dumps PHP variables to YAML strings. + * + * @package symfony + * @subpackage yaml + * @author Fabien Potencier + * @version SVN: $Id: sfYamlDumper.class.php 10575 2008-08-01 13:08:42Z nicolas $ + */ +class sfYamlDumper +{ + /** + * Dumps a PHP value to YAML. + * + * @param mixed $input The PHP value + * @param integer $inline The level where you switch to inline YAML + * @param integer $indent The level o indentation indentation (used internally) + * + * @return string The YAML representation of the PHP value + */ + public function dump($input, $inline = 0, $indent = 0) + { + $output = ''; + $prefix = $indent ? str_repeat(' ', $indent) : ''; + + if ($inline <= 0 || !is_array($input) || empty($input)) + { + $output .= $prefix.sfYamlInline::dump($input); + } + else + { + $isAHash = array_keys($input) !== range(0, count($input) - 1); + + foreach ($input as $key => $value) + { + $willBeInlined = $inline - 1 <= 0 || !is_array($value) || empty($value); + + $output .= sprintf('%s%s%s%s', + $prefix, + $isAHash ? sfYamlInline::dump($key).':' : '-', + $willBeInlined ? ' ' : "\n", + $this->dump($value, $inline - 1, $willBeInlined ? 0 : $indent + 2) + ).($willBeInlined ? "\n" : ''); + } + } + + return $output; + } +} diff --git a/3rdparty/aws-sdk/lib/yaml/lib/sfYamlInline.php b/3rdparty/aws-sdk/lib/yaml/lib/sfYamlInline.php new file mode 100644 index 0000000000..66edb4f07a --- /dev/null +++ b/3rdparty/aws-sdk/lib/yaml/lib/sfYamlInline.php @@ -0,0 +1,442 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +require_once dirname(__FILE__).'/sfYaml.php'; + +/** + * sfYamlInline implements a YAML parser/dumper for the YAML inline syntax. + * + * @package symfony + * @subpackage yaml + * @author Fabien Potencier + * @version SVN: $Id: sfYamlInline.class.php 16177 2009-03-11 08:32:48Z fabien $ + */ +class sfYamlInline +{ + const REGEX_QUOTED_STRING = '(?:"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'([^\']*(?:\'\'[^\']*)*)\')'; + + /** + * Convert a YAML string to a PHP array. + * + * @param string $value A YAML string + * + * @return array A PHP array representing the YAML string + */ + static public function load($value) + { + $value = trim($value); + + if (0 == strlen($value)) + { + return ''; + } + + if (function_exists('mb_internal_encoding') && ((int) ini_get('mbstring.func_overload')) & 2) + { + $mbEncoding = mb_internal_encoding(); + mb_internal_encoding('ASCII'); + } + + switch ($value[0]) + { + case '[': + $result = self::parseSequence($value); + break; + case '{': + $result = self::parseMapping($value); + break; + default: + $result = self::parseScalar($value); + } + + if (isset($mbEncoding)) + { + mb_internal_encoding($mbEncoding); + } + + return $result; + } + + /** + * Dumps a given PHP variable to a YAML string. + * + * @param mixed $value The PHP variable to convert + * + * @return string The YAML string representing the PHP array + */ + static public function dump($value) + { + if ('1.1' === sfYaml::getSpecVersion()) + { + $trueValues = array('true', 'on', '+', 'yes', 'y'); + $falseValues = array('false', 'off', '-', 'no', 'n'); + } + else + { + $trueValues = array('true'); + $falseValues = array('false'); + } + + switch (true) + { + case is_resource($value): + throw new InvalidArgumentException('Unable to dump PHP resources in a YAML file.'); + case is_object($value): + return '!!php/object:'.serialize($value); + case is_array($value): + return self::dumpArray($value); + case null === $value: + return 'null'; + case true === $value: + return 'true'; + case false === $value: + return 'false'; + case ctype_digit($value): + return is_string($value) ? "'$value'" : (int) $value; + case is_numeric($value): + return is_infinite($value) ? str_ireplace('INF', '.Inf', strval($value)) : (is_string($value) ? "'$value'" : $value); + case false !== strpos($value, "\n") || false !== strpos($value, "\r"): + return sprintf('"%s"', str_replace(array('"', "\n", "\r"), array('\\"', '\n', '\r'), $value)); + case preg_match('/[ \s \' " \: \{ \} \[ \] , & \* \# \?] | \A[ - ? | < > = ! % @ ` ]/x', $value): + return sprintf("'%s'", str_replace('\'', '\'\'', $value)); + case '' == $value: + return "''"; + case preg_match(self::getTimestampRegex(), $value): + return "'$value'"; + case in_array(strtolower($value), $trueValues): + return "'$value'"; + case in_array(strtolower($value), $falseValues): + return "'$value'"; + case in_array(strtolower($value), array('null', '~')): + return "'$value'"; + default: + return $value; + } + } + + /** + * Dumps a PHP array to a YAML string. + * + * @param array $value The PHP array to dump + * + * @return string The YAML string representing the PHP array + */ + static protected function dumpArray($value) + { + // array + $keys = array_keys($value); + if ( + (1 == count($keys) && '0' == $keys[0]) + || + (count($keys) > 1 && array_reduce($keys, create_function('$v,$w', 'return (integer) $v + $w;'), 0) == count($keys) * (count($keys) - 1) / 2)) + { + $output = array(); + foreach ($value as $val) + { + $output[] = self::dump($val); + } + + return sprintf('[%s]', implode(', ', $output)); + } + + // mapping + $output = array(); + foreach ($value as $key => $val) + { + $output[] = sprintf('%s: %s', self::dump($key), self::dump($val)); + } + + return sprintf('{ %s }', implode(', ', $output)); + } + + /** + * Parses a scalar to a YAML string. + * + * @param scalar $scalar + * @param string $delimiters + * @param array $stringDelimiter + * @param integer $i + * @param boolean $evaluate + * + * @return string A YAML string + */ + static public function parseScalar($scalar, $delimiters = null, $stringDelimiters = array('"', "'"), &$i = 0, $evaluate = true) + { + if (in_array($scalar[$i], $stringDelimiters)) + { + // quoted scalar + $output = self::parseQuotedScalar($scalar, $i); + } + else + { + // "normal" string + if (!$delimiters) + { + $output = substr($scalar, $i); + $i += strlen($output); + + // remove comments + if (false !== $strpos = strpos($output, ' #')) + { + $output = rtrim(substr($output, 0, $strpos)); + } + } + else if (preg_match('/^(.+?)('.implode('|', $delimiters).')/', substr($scalar, $i), $match)) + { + $output = $match[1]; + $i += strlen($output); + } + else + { + throw new InvalidArgumentException(sprintf('Malformed inline YAML string (%s).', $scalar)); + } + + $output = $evaluate ? self::evaluateScalar($output) : $output; + } + + return $output; + } + + /** + * Parses a quoted scalar to YAML. + * + * @param string $scalar + * @param integer $i + * + * @return string A YAML string + */ + static protected function parseQuotedScalar($scalar, &$i) + { + if (!preg_match('/'.self::REGEX_QUOTED_STRING.'/A', substr($scalar, $i), $match)) + { + throw new InvalidArgumentException(sprintf('Malformed inline YAML string (%s).', substr($scalar, $i))); + } + + $output = substr($match[0], 1, strlen($match[0]) - 2); + + if ('"' == $scalar[$i]) + { + // evaluate the string + $output = str_replace(array('\\"', '\\n', '\\r'), array('"', "\n", "\r"), $output); + } + else + { + // unescape ' + $output = str_replace('\'\'', '\'', $output); + } + + $i += strlen($match[0]); + + return $output; + } + + /** + * Parses a sequence to a YAML string. + * + * @param string $sequence + * @param integer $i + * + * @return string A YAML string + */ + static protected function parseSequence($sequence, &$i = 0) + { + $output = array(); + $len = strlen($sequence); + $i += 1; + + // [foo, bar, ...] + while ($i < $len) + { + switch ($sequence[$i]) + { + case '[': + // nested sequence + $output[] = self::parseSequence($sequence, $i); + break; + case '{': + // nested mapping + $output[] = self::parseMapping($sequence, $i); + break; + case ']': + return $output; + case ',': + case ' ': + break; + default: + $isQuoted = in_array($sequence[$i], array('"', "'")); + $value = self::parseScalar($sequence, array(',', ']'), array('"', "'"), $i); + + if (!$isQuoted && false !== strpos($value, ': ')) + { + // embedded mapping? + try + { + $value = self::parseMapping('{'.$value.'}'); + } + catch (InvalidArgumentException $e) + { + // no, it's not + } + } + + $output[] = $value; + + --$i; + } + + ++$i; + } + + throw new InvalidArgumentException(sprintf('Malformed inline YAML string %s', $sequence)); + } + + /** + * Parses a mapping to a YAML string. + * + * @param string $mapping + * @param integer $i + * + * @return string A YAML string + */ + static protected function parseMapping($mapping, &$i = 0) + { + $output = array(); + $len = strlen($mapping); + $i += 1; + + // {foo: bar, bar:foo, ...} + while ($i < $len) + { + switch ($mapping[$i]) + { + case ' ': + case ',': + ++$i; + continue 2; + case '}': + return $output; + } + + // key + $key = self::parseScalar($mapping, array(':', ' '), array('"', "'"), $i, false); + + // value + $done = false; + while ($i < $len) + { + switch ($mapping[$i]) + { + case '[': + // nested sequence + $output[$key] = self::parseSequence($mapping, $i); + $done = true; + break; + case '{': + // nested mapping + $output[$key] = self::parseMapping($mapping, $i); + $done = true; + break; + case ':': + case ' ': + break; + default: + $output[$key] = self::parseScalar($mapping, array(',', '}'), array('"', "'"), $i); + $done = true; + --$i; + } + + ++$i; + + if ($done) + { + continue 2; + } + } + } + + throw new InvalidArgumentException(sprintf('Malformed inline YAML string %s', $mapping)); + } + + /** + * Evaluates scalars and replaces magic values. + * + * @param string $scalar + * + * @return string A YAML string + */ + static protected function evaluateScalar($scalar) + { + $scalar = trim($scalar); + + if ('1.1' === sfYaml::getSpecVersion()) + { + $trueValues = array('true', 'on', '+', 'yes', 'y'); + $falseValues = array('false', 'off', '-', 'no', 'n'); + } + else + { + $trueValues = array('true'); + $falseValues = array('false'); + } + + switch (true) + { + case 'null' == strtolower($scalar): + case '' == $scalar: + case '~' == $scalar: + return null; + case 0 === strpos($scalar, '!str'): + return (string) substr($scalar, 5); + case 0 === strpos($scalar, '! '): + return intval(self::parseScalar(substr($scalar, 2))); + case 0 === strpos($scalar, '!!php/object:'): + return unserialize(substr($scalar, 13)); + case ctype_digit($scalar): + $raw = $scalar; + $cast = intval($scalar); + return '0' == $scalar[0] ? octdec($scalar) : (((string) $raw == (string) $cast) ? $cast : $raw); + case in_array(strtolower($scalar), $trueValues): + return true; + case in_array(strtolower($scalar), $falseValues): + return false; + case is_numeric($scalar): + return '0x' == $scalar[0].$scalar[1] ? hexdec($scalar) : floatval($scalar); + case 0 == strcasecmp($scalar, '.inf'): + case 0 == strcasecmp($scalar, '.NaN'): + return -log(0); + case 0 == strcasecmp($scalar, '-.inf'): + return log(0); + case preg_match('/^(-|\+)?[0-9,]+(\.[0-9]+)?$/', $scalar): + return floatval(str_replace(',', '', $scalar)); + case preg_match(self::getTimestampRegex(), $scalar): + return strtotime($scalar); + default: + return (string) $scalar; + } + } + + static protected function getTimestampRegex() + { + return <<[0-9][0-9][0-9][0-9]) + -(?P[0-9][0-9]?) + -(?P[0-9][0-9]?) + (?:(?:[Tt]|[ \t]+) + (?P[0-9][0-9]?) + :(?P[0-9][0-9]) + :(?P[0-9][0-9]) + (?:\.(?P[0-9]*))? + (?:[ \t]*(?PZ|(?P[-+])(?P[0-9][0-9]?) + (?::(?P[0-9][0-9]))?))?)? + $~x +EOF; + } +} diff --git a/3rdparty/aws-sdk/lib/yaml/lib/sfYamlParser.php b/3rdparty/aws-sdk/lib/yaml/lib/sfYamlParser.php new file mode 100644 index 0000000000..4c56a56db0 --- /dev/null +++ b/3rdparty/aws-sdk/lib/yaml/lib/sfYamlParser.php @@ -0,0 +1,612 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +require_once(dirname(__FILE__).'/sfYamlInline.php'); + +if (!defined('PREG_BAD_UTF8_OFFSET_ERROR')) +{ + define('PREG_BAD_UTF8_OFFSET_ERROR', 5); +} + +/** + * sfYamlParser parses YAML strings to convert them to PHP arrays. + * + * @package symfony + * @subpackage yaml + * @author Fabien Potencier + * @version SVN: $Id: sfYamlParser.class.php 10832 2008-08-13 07:46:08Z fabien $ + */ +class sfYamlParser +{ + protected + $offset = 0, + $lines = array(), + $currentLineNb = -1, + $currentLine = '', + $refs = array(); + + /** + * Constructor + * + * @param integer $offset The offset of YAML document (used for line numbers in error messages) + */ + public function __construct($offset = 0) + { + $this->offset = $offset; + } + + /** + * Parses a YAML string to a PHP value. + * + * @param string $value A YAML string + * + * @return mixed A PHP value + * + * @throws InvalidArgumentException If the YAML is not valid + */ + public function parse($value) + { + $value = str_replace("\t", ' ', $value); // Convert tabs to spaces. + + $this->currentLineNb = -1; + $this->currentLine = ''; + $this->lines = explode("\n", $this->cleanup($value)); + + if (function_exists('mb_internal_encoding') && ((int) ini_get('mbstring.func_overload')) & 2) + { + $mbEncoding = mb_internal_encoding(); + mb_internal_encoding('ASCII'); + } + + $data = array(); + while ($this->moveToNextLine()) + { + if ($this->isCurrentLineEmpty()) + { + continue; + } + + // tab? + if (preg_match('#^\t+#', $this->currentLine)) + { + throw new InvalidArgumentException(sprintf('A YAML file cannot contain tabs as indentation at line %d (%s).', $this->getRealCurrentLineNb() + 1, $this->currentLine)); + } + + $isRef = $isInPlace = $isProcessed = false; + if (preg_match('#^\-((?P\s+)(?P.+?))?\s*$#', $this->currentLine, $values)) + { + if (isset($values['value']) && preg_match('#^&(?P[^ ]+) *(?P.*)#', $values['value'], $matches)) + { + $isRef = $matches['ref']; + $values['value'] = $matches['value']; + } + + // array + if (!isset($values['value']) || '' == trim($values['value'], ' ') || 0 === strpos(ltrim($values['value'], ' '), '#')) + { + $c = $this->getRealCurrentLineNb() + 1; + $parser = new sfYamlParser($c); + $parser->refs =& $this->refs; + $data[] = $parser->parse($this->getNextEmbedBlock()); + } + else + { + if (isset($values['leadspaces']) + && ' ' == $values['leadspaces'] + && preg_match('#^(?P'.sfYamlInline::REGEX_QUOTED_STRING.'|[^ \'"\{].*?) *\:(\s+(?P.+?))?\s*$#', $values['value'], $matches)) + { + // this is a compact notation element, add to next block and parse + $c = $this->getRealCurrentLineNb(); + $parser = new sfYamlParser($c); + $parser->refs =& $this->refs; + + $block = $values['value']; + if (!$this->isNextLineIndented()) + { + $block .= "\n".$this->getNextEmbedBlock($this->getCurrentLineIndentation() + 2); + } + + $data[] = $parser->parse($block); + } + else + { + $data[] = $this->parseValue($values['value']); + } + } + } + else if (preg_match('#^(?P'.sfYamlInline::REGEX_QUOTED_STRING.'|[^ \'"].*?) *\:(\s+(?P.+?))?\s*$#', $this->currentLine, $values)) + { + $key = sfYamlInline::parseScalar($values['key']); + + if ('<<' === $key) + { + if (isset($values['value']) && '*' === substr($values['value'], 0, 1)) + { + $isInPlace = substr($values['value'], 1); + if (!array_key_exists($isInPlace, $this->refs)) + { + throw new InvalidArgumentException(sprintf('Reference "%s" does not exist at line %s (%s).', $isInPlace, $this->getRealCurrentLineNb() + 1, $this->currentLine)); + } + } + else + { + if (isset($values['value']) && $values['value'] !== '') + { + $value = $values['value']; + } + else + { + $value = $this->getNextEmbedBlock(); + } + $c = $this->getRealCurrentLineNb() + 1; + $parser = new sfYamlParser($c); + $parser->refs =& $this->refs; + $parsed = $parser->parse($value); + + $merged = array(); + if (!is_array($parsed)) + { + throw new InvalidArgumentException(sprintf("YAML merge keys used with a scalar value instead of an array at line %s (%s)", $this->getRealCurrentLineNb() + 1, $this->currentLine)); + } + else if (isset($parsed[0])) + { + // Numeric array, merge individual elements + foreach (array_reverse($parsed) as $parsedItem) + { + if (!is_array($parsedItem)) + { + throw new InvalidArgumentException(sprintf("Merge items must be arrays at line %s (%s).", $this->getRealCurrentLineNb() + 1, $parsedItem)); + } + $merged = array_merge($parsedItem, $merged); + } + } + else + { + // Associative array, merge + $merged = array_merge($merge, $parsed); + } + + $isProcessed = $merged; + } + } + else if (isset($values['value']) && preg_match('#^&(?P[^ ]+) *(?P.*)#', $values['value'], $matches)) + { + $isRef = $matches['ref']; + $values['value'] = $matches['value']; + } + + if ($isProcessed) + { + // Merge keys + $data = $isProcessed; + } + // hash + else if (!isset($values['value']) || '' == trim($values['value'], ' ') || 0 === strpos(ltrim($values['value'], ' '), '#')) + { + // if next line is less indented or equal, then it means that the current value is null + if ($this->isNextLineIndented()) + { + $data[$key] = null; + } + else + { + $c = $this->getRealCurrentLineNb() + 1; + $parser = new sfYamlParser($c); + $parser->refs =& $this->refs; + $data[$key] = $parser->parse($this->getNextEmbedBlock()); + } + } + else + { + if ($isInPlace) + { + $data = $this->refs[$isInPlace]; + } + else + { + $data[$key] = $this->parseValue($values['value']); + } + } + } + else + { + // 1-liner followed by newline + if (2 == count($this->lines) && empty($this->lines[1])) + { + $value = sfYamlInline::load($this->lines[0]); + if (is_array($value)) + { + $first = reset($value); + if ('*' === substr($first, 0, 1)) + { + $data = array(); + foreach ($value as $alias) + { + $data[] = $this->refs[substr($alias, 1)]; + } + $value = $data; + } + } + + if (isset($mbEncoding)) + { + mb_internal_encoding($mbEncoding); + } + + return $value; + } + + switch (preg_last_error()) + { + case PREG_INTERNAL_ERROR: + $error = 'Internal PCRE error on line'; + break; + case PREG_BACKTRACK_LIMIT_ERROR: + $error = 'pcre.backtrack_limit reached on line'; + break; + case PREG_RECURSION_LIMIT_ERROR: + $error = 'pcre.recursion_limit reached on line'; + break; + case PREG_BAD_UTF8_ERROR: + $error = 'Malformed UTF-8 data on line'; + break; + case PREG_BAD_UTF8_OFFSET_ERROR: + $error = 'Offset doesn\'t correspond to the begin of a valid UTF-8 code point on line'; + break; + default: + $error = 'Unable to parse line'; + } + + throw new InvalidArgumentException(sprintf('%s %d (%s).', $error, $this->getRealCurrentLineNb() + 1, $this->currentLine)); + } + + if ($isRef) + { + $this->refs[$isRef] = end($data); + } + } + + if (isset($mbEncoding)) + { + mb_internal_encoding($mbEncoding); + } + + return empty($data) ? null : $data; + } + + /** + * Returns the current line number (takes the offset into account). + * + * @return integer The current line number + */ + protected function getRealCurrentLineNb() + { + return $this->currentLineNb + $this->offset; + } + + /** + * Returns the current line indentation. + * + * @return integer The current line indentation + */ + protected function getCurrentLineIndentation() + { + return strlen($this->currentLine) - strlen(ltrim($this->currentLine, ' ')); + } + + /** + * Returns the next embed block of YAML. + * + * @param integer $indentation The indent level at which the block is to be read, or null for default + * + * @return string A YAML string + */ + protected function getNextEmbedBlock($indentation = null) + { + $this->moveToNextLine(); + + if (null === $indentation) + { + $newIndent = $this->getCurrentLineIndentation(); + + if (!$this->isCurrentLineEmpty() && 0 == $newIndent) + { + throw new InvalidArgumentException(sprintf('Indentation problem at line %d (%s)', $this->getRealCurrentLineNb() + 1, $this->currentLine)); + } + } + else + { + $newIndent = $indentation; + } + + $data = array(substr($this->currentLine, $newIndent)); + + while ($this->moveToNextLine()) + { + if ($this->isCurrentLineEmpty()) + { + if ($this->isCurrentLineBlank()) + { + $data[] = substr($this->currentLine, $newIndent); + } + + continue; + } + + $indent = $this->getCurrentLineIndentation(); + + if (preg_match('#^(?P *)$#', $this->currentLine, $match)) + { + // empty line + $data[] = $match['text']; + } + else if ($indent >= $newIndent) + { + $data[] = substr($this->currentLine, $newIndent); + } + else if (0 == $indent) + { + $this->moveToPreviousLine(); + + break; + } + else + { + throw new InvalidArgumentException(sprintf('Indentation problem at line %d (%s)', $this->getRealCurrentLineNb() + 1, $this->currentLine)); + } + } + + return implode("\n", $data); + } + + /** + * Moves the parser to the next line. + */ + protected function moveToNextLine() + { + if ($this->currentLineNb >= count($this->lines) - 1) + { + return false; + } + + $this->currentLine = $this->lines[++$this->currentLineNb]; + + return true; + } + + /** + * Moves the parser to the previous line. + */ + protected function moveToPreviousLine() + { + $this->currentLine = $this->lines[--$this->currentLineNb]; + } + + /** + * Parses a YAML value. + * + * @param string $value A YAML value + * + * @return mixed A PHP value + */ + protected function parseValue($value) + { + if ('*' === substr($value, 0, 1)) + { + if (false !== $pos = strpos($value, '#')) + { + $value = substr($value, 1, $pos - 2); + } + else + { + $value = substr($value, 1); + } + + if (!array_key_exists($value, $this->refs)) + { + throw new InvalidArgumentException(sprintf('Reference "%s" does not exist (%s).', $value, $this->currentLine)); + } + return $this->refs[$value]; + } + + if (preg_match('/^(?P\||>)(?P\+|\-|\d+|\+\d+|\-\d+|\d+\+|\d+\-)?(?P +#.*)?$/', $value, $matches)) + { + $modifiers = isset($matches['modifiers']) ? $matches['modifiers'] : ''; + + return $this->parseFoldedScalar($matches['separator'], preg_replace('#\d+#', '', $modifiers), intval(abs($modifiers))); + } + else + { + return sfYamlInline::load($value); + } + } + + /** + * Parses a folded scalar. + * + * @param string $separator The separator that was used to begin this folded scalar (| or >) + * @param string $indicator The indicator that was used to begin this folded scalar (+ or -) + * @param integer $indentation The indentation that was used to begin this folded scalar + * + * @return string The text value + */ + protected function parseFoldedScalar($separator, $indicator = '', $indentation = 0) + { + $separator = '|' == $separator ? "\n" : ' '; + $text = ''; + + $notEOF = $this->moveToNextLine(); + + while ($notEOF && $this->isCurrentLineBlank()) + { + $text .= "\n"; + + $notEOF = $this->moveToNextLine(); + } + + if (!$notEOF) + { + return ''; + } + + if (!preg_match('#^(?P'.($indentation ? str_repeat(' ', $indentation) : ' +').')(?P.*)$#', $this->currentLine, $matches)) + { + $this->moveToPreviousLine(); + + return ''; + } + + $textIndent = $matches['indent']; + $previousIndent = 0; + + $text .= $matches['text'].$separator; + while ($this->currentLineNb + 1 < count($this->lines)) + { + $this->moveToNextLine(); + + if (preg_match('#^(?P {'.strlen($textIndent).',})(?P.+)$#', $this->currentLine, $matches)) + { + if (' ' == $separator && $previousIndent != $matches['indent']) + { + $text = substr($text, 0, -1)."\n"; + } + $previousIndent = $matches['indent']; + + $text .= str_repeat(' ', $diff = strlen($matches['indent']) - strlen($textIndent)).$matches['text'].($diff ? "\n" : $separator); + } + else if (preg_match('#^(?P *)$#', $this->currentLine, $matches)) + { + $text .= preg_replace('#^ {1,'.strlen($textIndent).'}#', '', $matches['text'])."\n"; + } + else + { + $this->moveToPreviousLine(); + + break; + } + } + + if (' ' == $separator) + { + // replace last separator by a newline + $text = preg_replace('/ (\n*)$/', "\n$1", $text); + } + + switch ($indicator) + { + case '': + $text = preg_replace('#\n+$#s', "\n", $text); + break; + case '+': + break; + case '-': + $text = preg_replace('#\n+$#s', '', $text); + break; + } + + return $text; + } + + /** + * Returns true if the next line is indented. + * + * @return Boolean Returns true if the next line is indented, false otherwise + */ + protected function isNextLineIndented() + { + $currentIndentation = $this->getCurrentLineIndentation(); + $notEOF = $this->moveToNextLine(); + + while ($notEOF && $this->isCurrentLineEmpty()) + { + $notEOF = $this->moveToNextLine(); + } + + if (false === $notEOF) + { + return false; + } + + $ret = false; + if ($this->getCurrentLineIndentation() <= $currentIndentation) + { + $ret = true; + } + + $this->moveToPreviousLine(); + + return $ret; + } + + /** + * Returns true if the current line is blank or if it is a comment line. + * + * @return Boolean Returns true if the current line is empty or if it is a comment line, false otherwise + */ + protected function isCurrentLineEmpty() + { + return $this->isCurrentLineBlank() || $this->isCurrentLineComment(); + } + + /** + * Returns true if the current line is blank. + * + * @return Boolean Returns true if the current line is blank, false otherwise + */ + protected function isCurrentLineBlank() + { + return '' == trim($this->currentLine, ' '); + } + + /** + * Returns true if the current line is a comment line. + * + * @return Boolean Returns true if the current line is a comment line, false otherwise + */ + protected function isCurrentLineComment() + { + //checking explicitly the first char of the trim is faster than loops or strpos + $ltrimmedLine = ltrim($this->currentLine, ' '); + return $ltrimmedLine[0] === '#'; + } + + /** + * Cleanups a YAML string to be parsed. + * + * @param string $value The input YAML string + * + * @return string A cleaned up YAML string + */ + protected function cleanup($value) + { + $value = str_replace(array("\r\n", "\r"), "\n", $value); + + if (!preg_match("#\n$#", $value)) + { + $value .= "\n"; + } + + // strip YAML header + $count = 0; + $value = preg_replace('#^\%YAML[: ][\d\.]+.*\n#s', '', $value, -1, $count); + $this->offset += $count; + + // remove leading comments and/or --- + $trimmedValue = preg_replace('#^((\#.*?\n)|(\-\-\-.*?\n))*#s', '', $value, -1, $count); + if ($count == 1) + { + // items have been removed, update the offset + $this->offset += substr_count($value, "\n") - substr_count($trimmedValue, "\n"); + $value = $trimmedValue; + } + + return $value; + } +} diff --git a/3rdparty/aws-sdk/sdk.class.php b/3rdparty/aws-sdk/sdk.class.php new file mode 100755 index 0000000000..8dcb7bf252 --- /dev/null +++ b/3rdparty/aws-sdk/sdk.class.php @@ -0,0 +1,1435 @@ +). + */ + public $utilities_class = 'CFUtilities'; + + /** + * The default class to use for HTTP requests (defaults to ). + */ + public $request_class = 'CFRequest'; + + /** + * The default class to use for HTTP responses (defaults to ). + */ + public $response_class = 'CFResponse'; + + /** + * The default class to use for parsing XML (defaults to ). + */ + public $parser_class = 'CFSimpleXML'; + + /** + * The default class to use for handling batch requests (defaults to ). + */ + public $batch_class = 'CFBatchRequest'; + + /** + * The state of SSL/HTTPS use. + */ + public $use_ssl = true; + + /** + * The state of SSL certificate verification. + */ + public $ssl_verification = true; + + /** + * The proxy to use for connecting. + */ + public $proxy = null; + + /** + * The alternate hostname to use, if any. + */ + public $hostname = null; + + /** + * The state of the capability to override the hostname with . + */ + public $override_hostname = true; + + /** + * The alternate port number to use, if any. + */ + public $port_number = null; + + /** + * The alternate resource prefix to use, if any. + */ + public $resource_prefix = null; + + /** + * The state of cache flow usage. + */ + public $use_cache_flow = false; + + /** + * The caching class to use. + */ + public $cache_class = null; + + /** + * The caching location to use. + */ + public $cache_location = null; + + /** + * When the cache should be considered stale. + */ + public $cache_expires = null; + + /** + * The state of cache compression. + */ + public $cache_compress = null; + + /** + * The current instantiated cache object. + */ + public $cache_object = null; + + /** + * The current instantiated batch request object. + */ + public $batch_object = null; + + /** + * The internally instantiated batch request object. + */ + public $internal_batch_object = null; + + /** + * The state of batch flow usage. + */ + public $use_batch_flow = false; + + /** + * The state of the cache deletion setting. + */ + public $delete_cache = false; + + /** + * The state of the debug mode setting. + */ + public $debug_mode = false; + + /** + * The number of times to retry failed requests. + */ + public $max_retries = 3; + + /** + * The user-defined callback function to call when a stream is read from. + */ + public $registered_streaming_read_callback = null; + + /** + * The user-defined callback function to call when a stream is written to. + */ + public $registered_streaming_write_callback = null; + + /** + * The credentials to use for authentication. + */ + public $credentials = array(); + + /** + * The authentication class to use. + */ + public $auth_class = null; + + /** + * The operation to execute. + */ + public $operation = null; + + /** + * The payload to send. + */ + public $payload = array(); + + /** + * The string prefix to prepend to the operation name. + */ + public $operation_prefix = ''; + + /** + * The number of times a request has been retried. + */ + public $redirects = 0; + + /** + * The state of whether the response should be parsed or not. + */ + public $parse_the_response = true; + + + /*%******************************************************************************************%*/ + // CONSTRUCTOR + + /** + * The constructor. This class should not be instantiated directly. Rather, a service-specific class + * should be instantiated. + * + * @param array $options (Optional) An associative array of parameters that can have the following keys:
    + *
  • certificate_authority - boolean - Optional - Determines which Cerificate Authority file to use. A value of boolean false will use the Certificate Authority file available on the system. A value of boolean true will use the Certificate Authority provided by the SDK. Passing a file system path to a Certificate Authority file (chmodded to 0755) will use that. Leave this set to false if you're not sure.
  • + *
  • credentials - string - Optional - The name of the credential set to use for authentication.
  • + *
  • default_cache_config - string - Optional - This option allows a preferred storage type to be configured for long-term caching. This can be changed later using the method. Valid values are: apc, xcache, or a file system path such as ./cache or /tmp/cache/.
  • + *
  • key - string - Optional - Your AWS key, or a session key. If blank, the default credential set will be used.
  • + *
  • secret - string - Optional - Your AWS secret key, or a session secret key. If blank, the default credential set will be used.
  • + *
  • token - string - Optional - An AWS session token.
+ * @return void + */ + public function __construct(array $options = array()) + { + // Instantiate the utilities class. + $this->util = new $this->utilities_class(); + + // Determine the current service. + $this->service = get_class($this); + + // Create credentials based on the options + $instance_credentials = new CFCredential($options); + + // Retreive a credential set from config.inc.php if it exists + if (isset($options['credentials'])) + { + // Use a specific credential set and merge with the instance credentials + $this->credentials = CFCredentials::get($options['credentials']) + ->merge($instance_credentials); + } + else + { + try + { + // Use the default credential set and merge with the instance credentials + $this->credentials = CFCredentials::get(CFCredentials::DEFAULT_KEY) + ->merge($instance_credentials); + } + catch (CFCredentials_Exception $e) + { + if (isset($options['key']) && isset($options['secret'])) + { + // Only the instance credentials were provided + $this->credentials = $instance_credentials; + } + else + { + // No credentials provided in the config file or constructor + throw new CFCredentials_Exception('No credentials were provided to ' . $this->service . '.'); + } + } + } + + // Set internal credentials after they are resolved + $this->key = $this->credentials->key; + $this->secret_key = $this->credentials->secret; + $this->auth_token = $this->credentials->token; + + // Automatically enable whichever caching mechanism is set to default. + $this->set_cache_config($this->credentials->default_cache_config); + } + + /** + * Alternate approach to constructing a new instance. Supports chaining. + * + * @param array $options (Optional) An associative array of parameters that can have the following keys:
    + *
  • certificate_authority - boolean - Optional - Determines which Cerificate Authority file to use. A value of boolean false will use the Certificate Authority file available on the system. A value of boolean true will use the Certificate Authority provided by the SDK. Passing a file system path to a Certificate Authority file (chmodded to 0755) will use that. Leave this set to false if you're not sure.
  • + *
  • credentials - string - Optional - The name of the credential set to use for authentication.
  • + *
  • default_cache_config - string - Optional - This option allows a preferred storage type to be configured for long-term caching. This can be changed later using the method. Valid values are: apc, xcache, or a file system path such as ./cache or /tmp/cache/.
  • + *
  • key - string - Optional - Your AWS key, or a session key. If blank, the default credential set will be used.
  • + *
  • secret - string - Optional - Your AWS secret key, or a session secret key. If blank, the default credential set will be used.
  • + *
  • token - string - Optional - An AWS session token.
+ * @return void + */ + public static function factory(array $options = array()) + { + if (version_compare(PHP_VERSION, '5.3.0', '<')) + { + throw new Exception('PHP 5.3 or newer is required to instantiate a new class with CLASS::factory().'); + } + + $self = get_called_class(); + return new $self($options); + } + + + /*%******************************************************************************************%*/ + // MAGIC METHODS + + /** + * A magic method that allows `camelCase` method names to be translated into `snake_case` names. + * + * @param string $name (Required) The name of the method. + * @param array $arguments (Required) The arguments passed to the method. + * @return mixed The results of the intended method. + */ + public function __call($name, $arguments) + { + // Convert camelCase method calls to snake_case. + $method_name = strtolower(preg_replace('/([a-z])([A-Z])/', '$1_$2', $name)); + + if (method_exists($this, $method_name)) + { + return call_user_func_array(array($this, $method_name), $arguments); + } + + throw new CFRuntime_Exception('The method ' . $name . '() is undefined. Attempted to map to ' . $method_name . '() which is also undefined. Error occurred'); + } + + + /*%******************************************************************************************%*/ + // SET CUSTOM SETTINGS + + /** + * Set the proxy settings to use. + * + * @param string $proxy (Required) Accepts proxy credentials in the following format: `proxy://user:pass@hostname:port` + * @return $this A reference to the current instance. + */ + public function set_proxy($proxy) + { + $this->proxy = $proxy; + return $this; + } + + /** + * Set the hostname to connect to. This is useful for alternate services that are API-compatible with + * AWS, but run from a different hostname. + * + * @param string $hostname (Required) The alternate hostname to use in place of the default one. Useful for mock or test applications living on different hostnames. + * @param integer $port_number (Optional) The alternate port number to use in place of the default one. Useful for mock or test applications living on different port numbers. + * @return $this A reference to the current instance. + */ + public function set_hostname($hostname, $port_number = null) + { + if ($this->override_hostname) + { + $this->hostname = $hostname; + + if ($port_number) + { + $this->port_number = $port_number; + $this->hostname .= ':' . (string) $this->port_number; + } + } + + return $this; + } + + /** + * Set the resource prefix to use. This method is useful for alternate services that are API-compatible + * with AWS. + * + * @param string $prefix (Required) An alternate prefix to prepend to the resource path. Useful for mock or test applications. + * @return $this A reference to the current instance. + */ + public function set_resource_prefix($prefix) + { + $this->resource_prefix = $prefix; + return $this; + } + + /** + * Disables any subsequent use of the method. + * + * @param boolean $override (Optional) Whether or not subsequent calls to should be obeyed. A `false` value disables the further effectiveness of . Defaults to `true`. + * @return $this A reference to the current instance. + */ + public function allow_hostname_override($override = true) + { + $this->override_hostname = $override; + return $this; + } + + /** + * Disables SSL/HTTPS connections for hosts that don't support them. Some services, however, still + * require SSL support. + * + * This method will throw a user warning when invoked, which can be hidden by changing your + * settings. + * + * @return $this A reference to the current instance. + */ + public function disable_ssl() + { + trigger_error('Disabling SSL connections is potentially unsafe and highly discouraged.', E_USER_WARNING); + $this->use_ssl = false; + return $this; + } + + /** + * Disables the verification of the SSL Certificate Authority. Doing so can enable an attacker to carry + * out a man-in-the-middle attack. + * + * https://secure.wikimedia.org/wikipedia/en/wiki/Man-in-the-middle_attack + * + * This method will throw a user warning when invoked, which can be hidden by changing your + * settings. + * + * @return $this A reference to the current instance. + */ + public function disable_ssl_verification($ssl_verification = false) + { + trigger_error('Disabling the verification of SSL certificates can lead to man-in-the-middle attacks. It is potentially unsafe and highly discouraged.', E_USER_WARNING); + $this->ssl_verification = $ssl_verification; + return $this; + } + + /** + * Enables HTTP request/response header logging to `STDERR`. + * + * @param boolean $enabled (Optional) Whether or not to enable debug mode. Defaults to `true`. + * @return $this A reference to the current instance. + */ + public function enable_debug_mode($enabled = true) + { + $this->debug_mode = $enabled; + return $this; + } + + /** + * Sets the maximum number of times to retry failed requests. + * + * @param integer $retries (Optional) The maximum number of times to retry failed requests. Defaults to `3`. + * @return $this A reference to the current instance. + */ + public function set_max_retries($retries = 3) + { + $this->max_retries = $retries; + return $this; + } + + /** + * Set the caching configuration to use for response caching. + * + * @param string $location (Required)

The location to store the cache object in. This may vary by cache method.

  • File - The local file system paths such as ./cache (relative) or /tmp/cache/ (absolute). The location must be server-writable.
  • APC - Pass in apc to use this lightweight cache. You must have the APC extension installed.
  • XCache - Pass in xcache to use this lightweight cache. You must have the XCache extension installed.
  • Memcached - Pass in an indexed array of associative arrays. Each associative array should have a host and a port value representing a Memcached server to connect to.
  • PDO - A URL-style string (e.g. pdo.mysql://user:pass@localhost/cache) or a standard DSN-style string (e.g. pdo.sqlite:/sqlite/cache.db). MUST be prefixed with pdo.. See CachePDO and PDO for more details.
+ * @param boolean $gzip (Optional) Whether or not data should be gzipped before being stored. A value of `true` will compress the contents before caching them. A value of `false` will leave the contents uncompressed. Defaults to `true`. + * @return $this A reference to the current instance. + */ + public function set_cache_config($location, $gzip = true) + { + // If we have an array, we're probably passing in Memcached servers and ports. + if (is_array($location)) + { + $this->cache_class = 'CacheMC'; + } + else + { + // I would expect locations like `/tmp/cache`, `pdo.mysql://user:pass@hostname:port`, `pdo.sqlite:memory:`, and `apc`. + $type = strtolower(substr($location, 0, 3)); + switch ($type) + { + case 'apc': + $this->cache_class = 'CacheAPC'; + break; + + case 'xca': // First three letters of `xcache` + $this->cache_class = 'CacheXCache'; + break; + + case 'pdo': + $this->cache_class = 'CachePDO'; + $location = substr($location, 4); + break; + + default: + $this->cache_class = 'CacheFile'; + break; + } + } + + // Set the remaining cache information. + $this->cache_location = $location; + $this->cache_compress = $gzip; + + return $this; + } + + /** + * Register a callback function to execute whenever a data stream is read from using + * . + * + * The user-defined callback function should accept three arguments: + * + *
    + *
  • $curl_handle - resource - Required - The cURL handle resource that represents the in-progress transfer.
  • + *
  • $file_handle - resource - Required - The file handle resource that represents the file on the local file system.
  • + *
  • $length - integer - Required - The length in kilobytes of the data chunk that was transferred.
  • + *
+ * + * @param string|array|function $callback (Required) The callback function is called by , so you can pass the following values:
    + *
  • The name of a global function to execute, passed as a string.
  • + *
  • A method to execute, passed as array('ClassName', 'MethodName').
  • + *
  • An anonymous function (PHP 5.3+).
+ * @return $this A reference to the current instance. + */ + public function register_streaming_read_callback($callback) + { + $this->registered_streaming_read_callback = $callback; + return $this; + } + + /** + * Register a callback function to execute whenever a data stream is written to using + * . + * + * The user-defined callback function should accept two arguments: + * + *
    + *
  • $curl_handle - resource - Required - The cURL handle resource that represents the in-progress transfer.
  • + *
  • $length - integer - Required - The length in kilobytes of the data chunk that was transferred.
  • + *
+ * + * @param string|array|function $callback (Required) The callback function is called by , so you can pass the following values:
    + *
  • The name of a global function to execute, passed as a string.
  • + *
  • A method to execute, passed as array('ClassName', 'MethodName').
  • + *
  • An anonymous function (PHP 5.3+).
+ * @return $this A reference to the current instance. + */ + public function register_streaming_write_callback($callback) + { + $this->registered_streaming_write_callback = $callback; + return $this; + } + + /** + * Fetches and caches STS credentials. This is meant to be used by the constructor, and is not to be + * manually invoked. + * + * @param CacheCore $cache (Required) The a reference to the cache object that is being used to handle the caching. + * @param array $options (Required) The options that were passed into the constructor. + * @return mixed The data to be cached, or NULL. + */ + public function cache_sts_credentials($cache, $options) + { + $token = new AmazonSTS($options); + $response = $token->get_session_token(); + + if ($response->isOK()) + { + // Update the expiration + $expiration_time = strtotime((string) $response->body->GetSessionTokenResult->Credentials->Expiration); + $expiration_duration = round(($expiration_time - time()) * 0.85); + $cache->expire_in($expiration_duration); + + // Return the important data + return array( + 'key' => (string) $response->body->GetSessionTokenResult->Credentials->AccessKeyId, + 'secret' => (string) $response->body->GetSessionTokenResult->Credentials->SecretAccessKey, + 'token' => (string) $response->body->GetSessionTokenResult->Credentials->SessionToken, + 'expires' => (string) $response->body->GetSessionTokenResult->Credentials->Expiration, + ); + } + + return null; + } + + + /*%******************************************************************************************%*/ + // SET CUSTOM CLASSES + + /** + * Set a custom class for this functionality. Use this method when extending/overriding existing classes + * with new functionality. + * + * The replacement class must extend from . + * + * @param string $class (Optional) The name of the new class to use for this functionality. + * @return $this A reference to the current instance. + */ + public function set_utilities_class($class = 'CFUtilities') + { + $this->utilities_class = $class; + $this->util = new $this->utilities_class(); + return $this; + } + + /** + * Set a custom class for this functionality. Use this method when extending/overriding existing classes + * with new functionality. + * + * The replacement class must extend from . + * + * @param string $class (Optional) The name of the new class to use for this functionality. + * @param $this A reference to the current instance. + */ + public function set_request_class($class = 'CFRequest') + { + $this->request_class = $class; + return $this; + } + + /** + * Set a custom class for this functionality. Use this method when extending/overriding existing classes + * with new functionality. + * + * The replacement class must extend from . + * + * @param string $class (Optional) The name of the new class to use for this functionality. + * @return $this A reference to the current instance. + */ + public function set_response_class($class = 'CFResponse') + { + $this->response_class = $class; + return $this; + } + + /** + * Set a custom class for this functionality. Use this method when extending/overriding existing classes + * with new functionality. + * + * The replacement class must extend from . + * + * @param string $class (Optional) The name of the new class to use for this functionality. + * @return $this A reference to the current instance. + */ + public function set_parser_class($class = 'CFSimpleXML') + { + $this->parser_class = $class; + return $this; + } + + /** + * Set a custom class for this functionality. Use this method when extending/overriding existing classes + * with new functionality. + * + * The replacement class must extend from . + * + * @param string $class (Optional) The name of the new class to use for this functionality. + * @return $this A reference to the current instance. + */ + public function set_batch_class($class = 'CFBatchRequest') + { + $this->batch_class = $class; + return $this; + } + + + /*%******************************************************************************************%*/ + // AUTHENTICATION + + /** + * Default, shared method for authenticating a connection to AWS. + * + * @param string $operation (Required) Indicates the operation to perform. + * @param array $payload (Required) An associative array of parameters for authenticating. See the individual methods for allowed keys. + * @return CFResponse Object containing a parsed HTTP response. + */ + public function authenticate($operation, $payload) + { + $original_payload = $payload; + $method_arguments = func_get_args(); + $curlopts = array(); + $return_curl_handle = false; + + if (substr($operation, 0, strlen($this->operation_prefix)) !== $this->operation_prefix) + { + $operation = $this->operation_prefix . $operation; + } + + // Extract the custom CURLOPT settings from the payload + if (is_array($payload) && isset($payload['curlopts'])) + { + $curlopts = $payload['curlopts']; + unset($payload['curlopts']); + } + + // Determine whether the response or curl handle should be returned + if (is_array($payload) && isset($payload['returnCurlHandle'])) + { + $return_curl_handle = isset($payload['returnCurlHandle']) ? $payload['returnCurlHandle'] : false; + unset($payload['returnCurlHandle']); + } + + // Use the caching flow to determine if we need to do a round-trip to the server. + if ($this->use_cache_flow) + { + // Generate an identifier specific to this particular set of arguments. + $cache_id = $this->key . '_' . get_class($this) . '_' . $operation . '_' . sha1(serialize($method_arguments)); + + // Instantiate the appropriate caching object. + $this->cache_object = new $this->cache_class($cache_id, $this->cache_location, $this->cache_expires, $this->cache_compress); + + if ($this->delete_cache) + { + $this->use_cache_flow = false; + $this->delete_cache = false; + return $this->cache_object->delete(); + } + + // Invoke the cache callback function to determine whether to pull data from the cache or make a fresh request. + $data = $this->cache_object->response_manager(array($this, 'cache_callback'), $method_arguments); + + // Parse the XML body + $data = $this->parse_callback($data); + + // End! + return $data; + } + + /*%******************************************************************************************%*/ + + // Signer + $signer = new $this->auth_class($this->hostname, $operation, $payload, $this->credentials); + $signer->key = $this->key; + $signer->secret_key = $this->secret_key; + $signer->auth_token = $this->auth_token; + $signer->api_version = $this->api_version; + $signer->utilities_class = $this->utilities_class; + $signer->request_class = $this->request_class; + $signer->response_class = $this->response_class; + $signer->use_ssl = $this->use_ssl; + $signer->proxy = $this->proxy; + $signer->util = $this->util; + $signer->registered_streaming_read_callback = $this->registered_streaming_read_callback; + $signer->registered_streaming_write_callback = $this->registered_streaming_write_callback; + $request = $signer->authenticate(); + + // Update RequestCore settings + $request->request_class = $this->request_class; + $request->response_class = $this->response_class; + $request->ssl_verification = $this->ssl_verification; + + /*%******************************************************************************************%*/ + + // Debug mode + if ($this->debug_mode) + { + $request->debug_mode = $this->debug_mode; + } + + // Set custom CURLOPT settings + if (count($curlopts)) + { + $request->set_curlopts($curlopts); + } + + // Manage the (newer) batch request API or the (older) returnCurlHandle setting. + if ($this->use_batch_flow) + { + $handle = $request->prep_request(); + $this->batch_object->add($handle); + $this->use_batch_flow = false; + + return $handle; + } + elseif ($return_curl_handle) + { + return $request->prep_request(); + } + + // Send! + $request->send_request(); + + // Prepare the response. + $headers = $request->get_response_header(); + $headers['x-aws-stringtosign'] = $signer->string_to_sign; + + if (isset($signer->canonical_request)) + { + $headers['x-aws-canonicalrequest'] = $signer->canonical_request; + } + + $headers['x-aws-request-headers'] = $request->request_headers; + $headers['x-aws-body'] = $signer->querystring; + + $data = new $this->response_class($headers, ($this->parse_the_response === true) ? $this->parse_callback($request->get_response_body()) : $request->get_response_body(), $request->get_response_code()); + + // Was it Amazon's fault the request failed? Retry the request until we reach $max_retries. + if ( + (integer) $request->get_response_code() === 500 || // Internal Error (presumably transient) + (integer) $request->get_response_code() === 503) // Service Unavailable (presumably transient) + { + if ($this->redirects <= $this->max_retries) + { + // Exponential backoff + $delay = (integer) (pow(4, $this->redirects) * 100000); + usleep($delay); + $this->redirects++; + $data = $this->authenticate($operation, $original_payload); + } + } + + // DynamoDB has custom logic + elseif ( + (integer) $request->get_response_code() === 400 && + stripos((string) $request->get_response_body(), 'com.amazonaws.dynamodb.') !== false && ( + stripos((string) $request->get_response_body(), 'ProvisionedThroughputExceededException') !== false + ) + ) + { + if ($this->redirects === 0) + { + $this->redirects++; + $data = $this->authenticate($operation, $original_payload); + } + elseif ($this->redirects <= max($this->max_retries, 10)) + { + // Exponential backoff + $delay = (integer) (pow(2, ($this->redirects - 1)) * 50000); + usleep($delay); + $this->redirects++; + $data = $this->authenticate($operation, $original_payload); + } + } + + $this->redirects = 0; + return $data; + } + + + /*%******************************************************************************************%*/ + // BATCH REQUEST LAYER + + /** + * Specifies that the intended request should be queued for a later batch request. + * + * @param CFBatchRequest $queue (Optional) The instance to use for managing batch requests. If not available, it generates a new instance of . + * @return $this A reference to the current instance. + */ + public function batch(CFBatchRequest &$queue = null) + { + if ($queue) + { + $this->batch_object = $queue; + } + elseif ($this->internal_batch_object) + { + $this->batch_object = &$this->internal_batch_object; + } + else + { + $this->internal_batch_object = new $this->batch_class(); + $this->batch_object = &$this->internal_batch_object; + } + + $this->use_batch_flow = true; + + return $this; + } + + /** + * Executes the batch request queue by sending all queued requests. + * + * @param boolean $clear_after_send (Optional) Whether or not to clear the batch queue after sending a request. Defaults to `true`. Set this to `false` if you are caching batch responses and want to retrieve results later. + * @return array An array of objects. + */ + public function send($clear_after_send = true) + { + if ($this->use_batch_flow) + { + // When we send the request, disable batch flow. + $this->use_batch_flow = false; + + // If we're not caching, simply send the request. + if (!$this->use_cache_flow) + { + $response = $this->batch_object->send(); + $parsed_data = array_map(array($this, 'parse_callback'), $response); + $parsed_data = new CFArray($parsed_data); + + // Clear the queue + if ($clear_after_send) + { + $this->batch_object->queue = array(); + } + + return $parsed_data; + } + + // Generate an identifier specific to this particular set of arguments. + $cache_id = $this->key . '_' . get_class($this) . '_' . sha1(serialize($this->batch_object)); + + // Instantiate the appropriate caching object. + $this->cache_object = new $this->cache_class($cache_id, $this->cache_location, $this->cache_expires, $this->cache_compress); + + if ($this->delete_cache) + { + $this->use_cache_flow = false; + $this->delete_cache = false; + return $this->cache_object->delete(); + } + + // Invoke the cache callback function to determine whether to pull data from the cache or make a fresh request. + $data_set = $this->cache_object->response_manager(array($this, 'cache_callback_batch'), array($this->batch_object)); + $parsed_data = array_map(array($this, 'parse_callback'), $data_set); + $parsed_data = new CFArray($parsed_data); + + // Clear the queue + if ($clear_after_send) + { + $this->batch_object->queue = array(); + } + + // End! + return $parsed_data; + } + + // Load the class + $null = new CFBatchRequest(); + unset($null); + + throw new CFBatchRequest_Exception('You must use $object->batch()->send()'); + } + + /** + * Parses a response body into a PHP object if appropriate. + * + * @param CFResponse|string $response (Required) The object to parse, or an XML string that would otherwise be a response body. + * @param string $content_type (Optional) The content-type to use when determining how to parse the content. + * @return CFResponse|string A parsed object, or parsed XML. + */ + public function parse_callback($response, $headers = null) + { + // Bail out + if (!$this->parse_the_response) return $response; + + // Shorten this so we have a (mostly) single code path + if (isset($response->body)) + { + if (is_string($response->body)) + { + $body = $response->body; + } + else + { + return $response; + } + } + elseif (is_string($response)) + { + $body = $response; + } + else + { + return $response; + } + + // Decompress gzipped content + if (isset($headers['content-encoding'])) + { + switch (strtolower(trim($headers['content-encoding'], "\x09\x0A\x0D\x20"))) + { + case 'gzip': + case 'x-gzip': + $decoder = new CFGzipDecode($body); + if ($decoder->parse()) + { + $body = $decoder->data; + } + break; + + case 'deflate': + if (($uncompressed = gzuncompress($body)) !== false) + { + $body = $uncompressed; + } + elseif (($uncompressed = gzinflate($body)) !== false) + { + $body = $uncompressed; + } + break; + } + } + + // Look for XML cues + if ( + (isset($headers['content-type']) && ($headers['content-type'] === 'text/xml' || $headers['content-type'] === 'application/xml')) || // We know it's XML + (!isset($headers['content-type']) && (stripos($body, '') === 0) || preg_match('/^<(\w*) xmlns="http(s?):\/\/(\w*).amazon(aws)?.com/im', $body)) // Sniff for XML + ) + { + // Strip the default XML namespace to simplify XPath expressions + $body = str_replace("xmlns=", "ns=", $body); + + try { + // Parse the XML body + $body = new $this->parser_class($body); + } + catch (Exception $e) + { + throw new Parser_Exception($e->getMessage()); + } + } + // Look for JSON cues + elseif ( + (isset($headers['content-type']) && ($headers['content-type'] === 'application/json') || $headers['content-type'] === 'application/x-amz-json-1.0') || // We know it's JSON + (!isset($headers['content-type']) && $this->util->is_json($body)) // Sniff for JSON + ) + { + // Normalize JSON to a CFSimpleXML object + $body = CFJSON::to_xml($body, $this->parser_class); + } + + // Put the parsed data back where it goes + if (isset($response->body)) + { + $response->body = $body; + } + else + { + $response = $body; + } + + return $response; + } + + + /*%******************************************************************************************%*/ + // CACHING LAYER + + /** + * Specifies that the resulting object should be cached according to the settings from + * . + * + * @param string|integer $expires (Required) The time the cache is to expire. Accepts a number of seconds as an integer, or an amount of time, as a string, that is understood by (e.g. "1 hour"). + * @param $this A reference to the current instance. + * @return $this + */ + public function cache($expires) + { + // Die if they haven't used set_cache_config(). + if (!$this->cache_class) + { + throw new CFRuntime_Exception('Must call set_cache_config() before using cache()'); + } + + if (is_string($expires)) + { + $expires = strtotime($expires); + $this->cache_expires = $expires - time(); + } + elseif (is_int($expires)) + { + $this->cache_expires = $expires; + } + + $this->use_cache_flow = true; + + return $this; + } + + /** + * The callback function that is executed when the cache doesn't exist or has expired. The response of + * this method is cached. Accepts identical parameters as the method. Never call this + * method directly -- it is used internally by the caching system. + * + * @param string $operation (Required) Indicates the operation to perform. + * @param array $payload (Required) An associative array of parameters for authenticating. See the individual methods for allowed keys. + * @return CFResponse A parsed HTTP response. + */ + public function cache_callback($operation, $payload) + { + // Disable the cache flow since it's already been handled. + $this->use_cache_flow = false; + + // Make the request + $response = $this->authenticate($operation, $payload); + + // If this is an XML document, convert it back to a string. + if (isset($response->body) && ($response->body instanceof SimpleXMLElement)) + { + $response->body = $response->body->asXML(); + } + + return $response; + } + + /** + * Used for caching the results of a batch request. Never call this method directly; it is used + * internally by the caching system. + * + * @param CFBatchRequest $batch (Required) The batch request object to send. + * @return CFResponse A parsed HTTP response. + */ + public function cache_callback_batch(CFBatchRequest $batch) + { + return $batch->send(); + } + + /** + * Deletes a cached object using the specified cache storage type. + * + * @return boolean A value of `true` if cached object exists and is successfully deleted, otherwise `false`. + */ + public function delete_cache() + { + $this->use_cache_flow = true; + $this->delete_cache = true; + + return $this; + } +} + + +/** + * Contains the functionality for auto-loading service classes. + */ +class CFLoader +{ + /*%******************************************************************************************%*/ + // AUTO-LOADER + + /** + * Automatically load classes that aren't included. + * + * @param string $class (Required) The classname to load. + * @return boolean Whether or not the file was successfully loaded. + */ + public static function autoloader($class) + { + $path = dirname(__FILE__) . DIRECTORY_SEPARATOR; + + // Amazon SDK classes + if (strstr($class, 'Amazon')) + { + if (file_exists($require_this = $path . 'services' . DIRECTORY_SEPARATOR . str_ireplace('Amazon', '', strtolower($class)) . '.class.php')) + { + require_once $require_this; + return true; + } + + return false; + } + + // Utility classes + elseif (strstr($class, 'CF')) + { + if (file_exists($require_this = $path . 'utilities' . DIRECTORY_SEPARATOR . str_ireplace('CF', '', strtolower($class)) . '.class.php')) + { + require_once $require_this; + return true; + } + + return false; + } + + // Load CacheCore + elseif (strstr($class, 'Cache')) + { + if (file_exists($require_this = $path . 'lib' . DIRECTORY_SEPARATOR . 'cachecore' . DIRECTORY_SEPARATOR . strtolower($class) . '.class.php')) + { + require_once $require_this; + return true; + } + + return false; + } + + // Load RequestCore + elseif (strstr($class, 'RequestCore') || strstr($class, 'ResponseCore')) + { + if (file_exists($require_this = $path . 'lib' . DIRECTORY_SEPARATOR . 'requestcore' . DIRECTORY_SEPARATOR . 'requestcore.class.php')) + { + require_once $require_this; + return true; + } + + return false; + } + + // Load array-to-domdocument + elseif (strstr($class, 'Array2DOM')) + { + if (file_exists($require_this = $path . 'lib' . DIRECTORY_SEPARATOR . 'dom' . DIRECTORY_SEPARATOR . 'ArrayToDOMDocument.php')) + { + require_once $require_this; + return true; + } + + return false; + } + + // Load Authentication Signers + elseif (strstr($class, 'Auth')) + { + if (file_exists($require_this = $path . 'authentication' . DIRECTORY_SEPARATOR . str_replace('auth', 'signature_', strtolower($class)) . '.class.php')) + { + require_once $require_this; + return true; + } + + return false; + } + + // Load Signer interface + elseif ($class === 'Signer') + { + if (!interface_exists('Signable', false) && + file_exists($require_this = $path . 'authentication' . DIRECTORY_SEPARATOR . 'signable.interface.php')) + { + require_once $require_this; + } + + if (file_exists($require_this = $path . 'authentication' . DIRECTORY_SEPARATOR . 'signer.abstract.php')) + { + require_once $require_this; + return true; + } + + return false; + } + + // Load Symfony YAML classes + elseif (strstr($class, 'sfYaml')) + { + if (file_exists($require_this = $path . 'lib' . DIRECTORY_SEPARATOR . 'yaml' . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR . 'sfYaml.php')) + { + require_once $require_this; + return true; + } + + return false; + } + + return false; + } +} + +// Register the autoloader. +spl_autoload_register(array('CFLoader', 'autoloader')); + +// Don't look for any configuration files, the Amazon S3 storage backend handles configuration + +// /*%******************************************************************************************%*/ +// // CONFIGURATION +// +// // Look for include file in the same directory (e.g. `./config.inc.php`). +// if (file_exists(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'config.inc.php')) +// { +// include_once dirname(__FILE__) . DIRECTORY_SEPARATOR . 'config.inc.php'; +// } +// // Fallback to `~/.aws/sdk/config.inc.php` +// else +// { +// if (!isset($_ENV['HOME']) && isset($_SERVER['HOME'])) +// { +// $_ENV['HOME'] = $_SERVER['HOME']; +// } +// elseif (!isset($_ENV['HOME']) && !isset($_SERVER['HOME'])) +// { +// $_ENV['HOME'] = `cd ~ && pwd`; +// if (!$_ENV['HOME']) +// { +// switch (strtolower(PHP_OS)) +// { +// case 'darwin': +// $_ENV['HOME'] = '/Users/' . get_current_user(); +// break; +// +// case 'windows': +// case 'winnt': +// case 'win32': +// $_ENV['HOME'] = 'c:' . DIRECTORY_SEPARATOR . 'Documents and Settings' . DIRECTORY_SEPARATOR . get_current_user(); +// break; +// +// default: +// $_ENV['HOME'] = '/home/' . get_current_user(); +// break; +// } +// } +// } +// +// if (getenv('HOME') && file_exists(getenv('HOME') . DIRECTORY_SEPARATOR . '.aws' . DIRECTORY_SEPARATOR . 'sdk' . DIRECTORY_SEPARATOR . 'config.inc.php')) +// { +// include_once getenv('HOME') . DIRECTORY_SEPARATOR . '.aws' . DIRECTORY_SEPARATOR . 'sdk' . DIRECTORY_SEPARATOR . 'config.inc.php'; +// } +// } diff --git a/3rdparty/aws-sdk/services/s3.class.php b/3rdparty/aws-sdk/services/s3.class.php new file mode 100755 index 0000000000..2e9e1cd52b --- /dev/null +++ b/3rdparty/aws-sdk/services/s3.class.php @@ -0,0 +1,3979 @@ + for more information. + * + * @version 2012.01.17 + * @license See the included NOTICE.md file for more information. + * @copyright See the included NOTICE.md file for more information. + * @link http://aws.amazon.com/s3/ Amazon Simple Storage Service + * @link http://aws.amazon.com/documentation/s3/ Amazon Simple Storage Service documentation + */ +class AmazonS3 extends CFRuntime +{ + /*%******************************************************************************************%*/ + // REGIONAL ENDPOINTS + + /** + * Specify the queue URL for the US-Standard (Northern Virginia & Washington State) Region. + */ + const REGION_US_E1 = 's3.amazonaws.com'; + + /** + * Specify the queue URL for the US-Standard (Northern Virginia & Washington State) Region. + */ + const REGION_VIRGINIA = self::REGION_US_E1; + + /** + * Specify the queue URL for the US-Standard (Northern Virginia & Washington State) Region. + */ + const REGION_US_STANDARD = self::REGION_US_E1; + + /** + * Specify the queue URL for the US-West 1 (Northern California) Region. + */ + const REGION_US_W1 = 's3-us-west-1.amazonaws.com'; + + /** + * Specify the queue URL for the US-West 1 (Northern California) Region. + */ + const REGION_CALIFORNIA = self::REGION_US_W1; + + /** + * Specify the queue URL for the US-West 2 (Oregon) Region. + */ + const REGION_US_W2 = 's3-us-west-2.amazonaws.com'; + + /** + * Specify the queue URL for the US-West 2 (Oregon) Region. + */ + const REGION_OREGON = self::REGION_US_W2; + + /** + * Specify the queue URL for the EU (Ireland) Region. + */ + const REGION_EU_W1 = 's3-eu-west-1.amazonaws.com'; + + /** + * Specify the queue URL for the EU (Ireland) Region. + */ + const REGION_IRELAND = self::REGION_EU_W1; + + /** + * Specify the queue URL for the Asia Pacific (Singapore) Region. + */ + const REGION_APAC_SE1 = 's3-ap-southeast-1.amazonaws.com'; + + /** + * Specify the queue URL for the Asia Pacific (Singapore) Region. + */ + const REGION_SINGAPORE = self::REGION_APAC_SE1; + + /** + * Specify the queue URL for the Asia Pacific (Japan) Region. + */ + const REGION_APAC_NE1 = 's3-ap-northeast-1.amazonaws.com'; + + /** + * Specify the queue URL for the Asia Pacific (Japan) Region. + */ + const REGION_TOKYO = self::REGION_APAC_NE1; + + /** + * Specify the queue URL for the South America (Sao Paulo) Region. + */ + const REGION_SA_E1 = 's3-sa-east-1.amazonaws.com'; + + /** + * Specify the queue URL for the South America (Sao Paulo) Region. + */ + const REGION_SAO_PAULO = self::REGION_SA_E1; + + /** + * Specify the queue URL for the United States GovCloud Region. + */ + const REGION_US_GOV1 = 's3-us-gov-west-1.amazonaws.com'; + + /** + * Specify the queue URL for the United States GovCloud FIPS 140-2 Region. + */ + const REGION_US_GOV1_FIPS = 's3-fips-us-gov-west-1.amazonaws.com'; + + /** + * The default endpoint. + */ + const DEFAULT_URL = self::REGION_US_E1; + + + /*%******************************************************************************************%*/ + // REGIONAL WEBSITE ENDPOINTS + + /** + * Specify the queue URL for the US-Standard (Northern Virginia & Washington State) Website Region. + */ + const REGION_US_E1_WEBSITE = 's3-website-us-east-1.amazonaws.com'; + + /** + * Specify the queue URL for the US-Standard (Northern Virginia & Washington State) Website Region. + */ + const REGION_VIRGINIA_WEBSITE = self::REGION_US_E1_WEBSITE; + + /** + * Specify the queue URL for the US-Standard (Northern Virginia & Washington State) Website Region. + */ + const REGION_US_STANDARD_WEBSITE = self::REGION_US_E1_WEBSITE; + + /** + * Specify the queue URL for the US-West 1 (Northern California) Website Region. + */ + const REGION_US_W1_WEBSITE = 's3-website-us-west-1.amazonaws.com'; + + /** + * Specify the queue URL for the US-West 1 (Northern California) Website Region. + */ + const REGION_CALIFORNIA_WEBSITE = self::REGION_US_W1_WEBSITE; + + /** + * Specify the queue URL for the US-West 2 (Oregon) Website Region. + */ + const REGION_US_W2_WEBSITE = 's3-website-us-west-2.amazonaws.com'; + + /** + * Specify the queue URL for the US-West 2 (Oregon) Website Region. + */ + const REGION_OREGON_WEBSITE = self::REGION_US_W2_WEBSITE; + + /** + * Specify the queue URL for the EU (Ireland) Website Region. + */ + const REGION_EU_W1_WEBSITE = 's3-website-eu-west-1.amazonaws.com'; + + /** + * Specify the queue URL for the EU (Ireland) Website Region. + */ + const REGION_IRELAND_WEBSITE = self::REGION_EU_W1_WEBSITE; + + /** + * Specify the queue URL for the Asia Pacific (Singapore) Website Region. + */ + const REGION_APAC_SE1_WEBSITE = 's3-website-ap-southeast-1.amazonaws.com'; + + /** + * Specify the queue URL for the Asia Pacific (Singapore) Website Region. + */ + const REGION_SINGAPORE_WEBSITE = self::REGION_APAC_SE1_WEBSITE; + + /** + * Specify the queue URL for the Asia Pacific (Japan) Website Region. + */ + const REGION_APAC_NE1_WEBSITE = 's3-website-ap-northeast-1.amazonaws.com'; + + /** + * Specify the queue URL for the Asia Pacific (Japan) Website Region. + */ + const REGION_TOKYO_WEBSITE = self::REGION_APAC_NE1_WEBSITE; + + /** + * Specify the queue URL for the South America (Sao Paulo) Website Region. + */ + const REGION_SA_E1_WEBSITE = 's3-website-sa-east-1.amazonaws.com'; + + /** + * Specify the queue URL for the South America (Sao Paulo) Website Region. + */ + const REGION_SAO_PAULO_WEBSITE = self::REGION_SA_E1_WEBSITE; + + /** + * Specify the queue URL for the United States GovCloud Website Region. + */ + const REGION_US_GOV1_WEBSITE = 's3-website-us-gov-west-1.amazonaws.com'; + + + /*%******************************************************************************************%*/ + // ACL + + /** + * ACL: Owner-only read/write. + */ + const ACL_PRIVATE = 'private'; + + /** + * ACL: Owner read/write, public read. + */ + const ACL_PUBLIC = 'public-read'; + + /** + * ACL: Public read/write. + */ + const ACL_OPEN = 'public-read-write'; + + /** + * ACL: Owner read/write, authenticated read. + */ + const ACL_AUTH_READ = 'authenticated-read'; + + /** + * ACL: Bucket owner read. + */ + const ACL_OWNER_READ = 'bucket-owner-read'; + + /** + * ACL: Bucket owner full control. + */ + const ACL_OWNER_FULL_CONTROL = 'bucket-owner-full-control'; + + + /*%******************************************************************************************%*/ + // GRANTS + + /** + * When applied to a bucket, grants permission to list the bucket. When applied to an object, this + * grants permission to read the object data and/or metadata. + */ + const GRANT_READ = 'READ'; + + /** + * When applied to a bucket, grants permission to create, overwrite, and delete any object in the + * bucket. This permission is not supported for objects. + */ + const GRANT_WRITE = 'WRITE'; + + /** + * Grants permission to read the ACL for the applicable bucket or object. The owner of a bucket or + * object always has this permission implicitly. + */ + const GRANT_READ_ACP = 'READ_ACP'; + + /** + * Gives permission to overwrite the ACP for the applicable bucket or object. The owner of a bucket + * or object always has this permission implicitly. Granting this permission is equivalent to granting + * FULL_CONTROL because the grant recipient can make any changes to the ACP. + */ + const GRANT_WRITE_ACP = 'WRITE_ACP'; + + /** + * Provides READ, WRITE, READ_ACP, and WRITE_ACP permissions. It does not convey additional rights and + * is provided only for convenience. + */ + const GRANT_FULL_CONTROL = 'FULL_CONTROL'; + + + /*%******************************************************************************************%*/ + // USERS + + /** + * The "AuthenticatedUsers" group for access control policies. + */ + const USERS_AUTH = 'http://acs.amazonaws.com/groups/global/AuthenticatedUsers'; + + /** + * The "AllUsers" group for access control policies. + */ + const USERS_ALL = 'http://acs.amazonaws.com/groups/global/AllUsers'; + + /** + * The "LogDelivery" group for access control policies. + */ + const USERS_LOGGING = 'http://acs.amazonaws.com/groups/s3/LogDelivery'; + + + /*%******************************************************************************************%*/ + // PATTERNS + + /** + * PCRE: Match all items + */ + const PCRE_ALL = '/.*/i'; + + + /*%******************************************************************************************%*/ + // STORAGE + + /** + * Standard storage redundancy. + */ + const STORAGE_STANDARD = 'STANDARD'; + + /** + * Reduced storage redundancy. + */ + const STORAGE_REDUCED = 'REDUCED_REDUNDANCY'; + + + /*%******************************************************************************************%*/ + // PROPERTIES + + /** + * The request URL. + */ + public $request_url; + + /** + * The virtual host setting. + */ + public $vhost; + + /** + * The base XML elements to use for access control policy methods. + */ + public $base_acp_xml; + + /** + * The base XML elements to use for creating buckets in regions. + */ + public $base_location_constraint; + + /** + * The base XML elements to use for logging methods. + */ + public $base_logging_xml; + + /** + * The base XML elements to use for notifications. + */ + public $base_notification_xml; + + /** + * The base XML elements to use for versioning. + */ + public $base_versioning_xml; + + /** + * The base XML elements to use for completing a multipart upload. + */ + public $complete_mpu_xml; + + /** + * The base XML elements to use for website support. + */ + public $website_config_xml; + + /** + * The base XML elements to use for multi-object delete support. + */ + public $multi_object_delete_xml; + + /** + * The base XML elements to use for object expiration support. + */ + public $object_expiration_xml; + + /** + * The DNS vs. Path-style setting. + */ + public $path_style = false; + + /** + * The state of whether the prefix change is temporary or permanent. + */ + public $temporary_prefix = false; + + + /*%******************************************************************************************%*/ + // CONSTRUCTOR + + /** + * Constructs a new instance of . + * + * @param array $options (Optional) An associative array of parameters that can have the following keys:
    + *
  • certificate_authority - boolean - Optional - Determines which Cerificate Authority file to use. A value of boolean false will use the Certificate Authority file available on the system. A value of boolean true will use the Certificate Authority provided by the SDK. Passing a file system path to a Certificate Authority file (chmodded to 0755) will use that. Leave this set to false if you're not sure.
  • + *
  • credentials - string - Optional - The name of the credential set to use for authentication.
  • + *
  • default_cache_config - string - Optional - This option allows a preferred storage type to be configured for long-term caching. This can be changed later using the method. Valid values are: apc, xcache, or a file system path such as ./cache or /tmp/cache/.
  • + *
  • key - string - Optional - Your AWS key, or a session key. If blank, the default credential set will be used.
  • + *
  • secret - string - Optional - Your AWS secret key, or a session secret key. If blank, the default credential set will be used.
  • + *
  • token - string - Optional - An AWS session token.
+ * @return void + */ + public function __construct(array $options = array()) + { + $this->vhost = null; + $this->api_version = '2006-03-01'; + $this->hostname = self::DEFAULT_URL; + + $this->base_acp_xml = ''; + $this->base_location_constraint = ''; + $this->base_logging_xml = ''; + $this->base_notification_xml = ''; + $this->base_versioning_xml = ''; + $this->complete_mpu_xml = ''; + $this->website_config_xml = 'index.htmlerror.html'; + $this->multi_object_delete_xml = ''; + $this->object_expiration_xml = ''; + + parent::__construct($options); + } + + + /*%******************************************************************************************%*/ + // AUTHENTICATION + + /** + * Authenticates a connection to Amazon S3. Do not use directly unless implementing custom methods for + * this class. + * + * @param string $operation (Required) The name of the bucket to operate on (S3 Only). + * @param array $payload (Required) An associative array of parameters for authenticating. See inline comments for allowed keys. + * @return CFResponse A object containing a parsed HTTP response. + * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/S3_Authentication.html REST authentication + */ + public function authenticate($operation, $payload) + { + /* + * Overriding or extending this class? You can pass the following "magic" keys into $opt. + * + * ## verb, resource, sub_resource and query_string ## + * /?& + * GET /filename.txt?versions&prefix=abc&max-items=1 + * + * ## versionId, uploadId, partNumber, response-* ## + * These don't follow the same rules as above, in that the they needs to be signed, while + * other query_string values do not. + * + * ## curlopts ## + * These values get passed directly to the cURL methods in RequestCore. + * + * ## fileUpload, fileDownload, seekTo ## + * These are slightly modified and then passed to the cURL methods in RequestCore. + * + * ## headers ## + * $opt['headers'] is an array, whose keys are HTTP headers to be sent. + * + * ## body ## + * This is the request body that is sent to the server via PUT/POST. + * + * ## preauth ## + * This is a hook that tells authenticate() to generate a pre-authenticated URL. + * + * ## returnCurlHandle ## + * Tells authenticate() to return the cURL handle for the request instead of executing it. + */ + + // Rename variables (to overcome inheritence issues) + $bucket = $operation; + $opt = $payload; + + // Validate the S3 bucket name + if (!$this->validate_bucketname_support($bucket)) + { + // @codeCoverageIgnoreStart + throw new S3_Exception('S3 does not support "' . $bucket . '" as a valid bucket name. Review "Bucket Restrictions and Limitations" in the S3 Developer Guide for more information.'); + // @codeCoverageIgnoreEnd + } + + // Die if $opt isn't set. + if (!$opt) return false; + + $method_arguments = func_get_args(); + + // Use the caching flow to determine if we need to do a round-trip to the server. + if ($this->use_cache_flow) + { + // Generate an identifier specific to this particular set of arguments. + $cache_id = $this->key . '_' . get_class($this) . '_' . $bucket . '_' . sha1(serialize($method_arguments)); + + // Instantiate the appropriate caching object. + $this->cache_object = new $this->cache_class($cache_id, $this->cache_location, $this->cache_expires, $this->cache_compress); + + if ($this->delete_cache) + { + $this->use_cache_flow = false; + $this->delete_cache = false; + return $this->cache_object->delete(); + } + + // Invoke the cache callback function to determine whether to pull data from the cache or make a fresh request. + $data = $this->cache_object->response_manager(array($this, 'cache_callback'), $method_arguments); + + if ($this->parse_the_response) + { + // Parse the XML body + $data = $this->parse_callback($data); + } + + // End! + return $data; + } + + // If we haven't already set a resource prefix and the bucket name isn't DNS-valid... + if ((!$this->resource_prefix && !$this->validate_bucketname_create($bucket)) || $this->path_style) + { + // Fall back to the older path-style URI + $this->set_resource_prefix('/' . $bucket); + $this->temporary_prefix = true; + } + + // Determine hostname + $scheme = $this->use_ssl ? 'https://' : 'http://'; + if ($this->resource_prefix || $this->path_style) // Use bucket-in-path method. + { + $hostname = $this->hostname . $this->resource_prefix . (($bucket === '' || $this->resource_prefix === '/' . $bucket) ? '' : ('/' . $bucket)); + } + else + { + $hostname = $this->vhost ? $this->vhost : (($bucket === '') ? $this->hostname : ($bucket . '.') . $this->hostname); + } + + // Get the UTC timestamp in RFC 2616 format + $date = gmdate(CFUtilities::DATE_FORMAT_RFC2616, time()); + + // Storage for request parameters. + $resource = ''; + $sub_resource = ''; + $querystringparams = array(); + $signable_querystringparams = array(); + $string_to_sign = ''; + $headers = array( + 'Content-MD5' => '', + 'Content-Type' => 'application/x-www-form-urlencoded', + 'Date' => $date + ); + + /*%******************************************************************************************%*/ + + // Do we have an authentication token? + if ($this->auth_token) + { + $headers['X-Amz-Security-Token'] = $this->auth_token; + } + + // Handle specific resources + if (isset($opt['resource'])) + { + $resource .= $opt['resource']; + } + + // Merge query string values + if (isset($opt['query_string'])) + { + $querystringparams = array_merge($querystringparams, $opt['query_string']); + } + $query_string = $this->util->to_query_string($querystringparams); + + // Merge the signable query string values. Must be alphabetical. + $signable_list = array( + 'partNumber', + 'response-cache-control', + 'response-content-disposition', + 'response-content-encoding', + 'response-content-language', + 'response-content-type', + 'response-expires', + 'uploadId', + 'versionId' + ); + foreach ($signable_list as $item) + { + if (isset($opt[$item])) + { + $signable_querystringparams[$item] = $opt[$item]; + } + } + $signable_query_string = $this->util->to_query_string($signable_querystringparams); + + // Merge the HTTP headers + if (isset($opt['headers'])) + { + $headers = array_merge($headers, $opt['headers']); + } + + // Compile the URI to request + $conjunction = '?'; + $signable_resource = '/' . str_replace('%2F', '/', rawurlencode($resource)); + $non_signable_resource = ''; + + if (isset($opt['sub_resource'])) + { + $signable_resource .= $conjunction . rawurlencode($opt['sub_resource']); + $conjunction = '&'; + } + if ($signable_query_string !== '') + { + $signable_query_string = $conjunction . $signable_query_string; + $conjunction = '&'; + } + if ($query_string !== '') + { + $non_signable_resource .= $conjunction . $query_string; + $conjunction = '&'; + } + if (substr($hostname, -1) === substr($signable_resource, 0, 1)) + { + $signable_resource = ltrim($signable_resource, '/'); + } + + $this->request_url = $scheme . $hostname . $signable_resource . $signable_query_string . $non_signable_resource; + + if (isset($opt['location'])) + { + $this->request_url = $opt['location']; + } + + // Gather information to pass along to other classes. + $helpers = array( + 'utilities' => $this->utilities_class, + 'request' => $this->request_class, + 'response' => $this->response_class, + ); + + // Instantiate the request class + $request = new $this->request_class($this->request_url, $this->proxy, $helpers, $this->credentials); + + // Update RequestCore settings + $request->request_class = $this->request_class; + $request->response_class = $this->response_class; + $request->ssl_verification = $this->ssl_verification; + + // Pass along registered stream callbacks + if ($this->registered_streaming_read_callback) + { + $request->register_streaming_read_callback($this->registered_streaming_read_callback); + } + + if ($this->registered_streaming_write_callback) + { + $request->register_streaming_write_callback($this->registered_streaming_write_callback); + } + + // Streaming uploads + if (isset($opt['fileUpload'])) + { + if (is_resource($opt['fileUpload'])) + { + // Determine the length to read from the stream + $length = null; // From current position until EOF by default, size determined by set_read_stream() + + if (isset($headers['Content-Length'])) + { + $length = $headers['Content-Length']; + } + elseif (isset($opt['seekTo'])) + { + // Read from seekTo until EOF by default + $stats = fstat($opt['fileUpload']); + + if ($stats && $stats['size'] >= 0) + { + $length = $stats['size'] - (integer) $opt['seekTo']; + } + } + + $request->set_read_stream($opt['fileUpload'], $length); + + if ($headers['Content-Type'] === 'application/x-www-form-urlencoded') + { + $headers['Content-Type'] = 'application/octet-stream'; + } + } + else + { + $request->set_read_file($opt['fileUpload']); + + // Determine the length to read from the file + $length = $request->read_stream_size; // The file size by default + + if (isset($headers['Content-Length'])) + { + $length = $headers['Content-Length']; + } + elseif (isset($opt['seekTo']) && isset($length)) + { + // Read from seekTo until EOF by default + $length -= (integer) $opt['seekTo']; + } + + $request->set_read_stream_size($length); + + // Attempt to guess the correct mime-type + if ($headers['Content-Type'] === 'application/x-www-form-urlencoded') + { + $extension = explode('.', $opt['fileUpload']); + $extension = array_pop($extension); + $mime_type = CFMimeTypes::get_mimetype($extension); + $headers['Content-Type'] = $mime_type; + } + } + + $headers['Content-Length'] = $request->read_stream_size; + $headers['Content-MD5'] = ''; + } + + // Handle streaming file offsets + if (isset($opt['seekTo'])) + { + // Pass the seek position to RequestCore + $request->set_seek_position((integer) $opt['seekTo']); + } + + // Streaming downloads + if (isset($opt['fileDownload'])) + { + if (is_resource($opt['fileDownload'])) + { + $request->set_write_stream($opt['fileDownload']); + } + else + { + $request->set_write_file($opt['fileDownload']); + } + } + + $curlopts = array(); + + // Set custom CURLOPT settings + if (isset($opt['curlopts'])) + { + $curlopts = $opt['curlopts']; + } + + // Debug mode + if ($this->debug_mode) + { + $curlopts[CURLOPT_VERBOSE] = true; + } + + // Set the curl options. + if (count($curlopts)) + { + $request->set_curlopts($curlopts); + } + + // Do we have a verb? + if (isset($opt['verb'])) + { + $request->set_method($opt['verb']); + $string_to_sign .= $opt['verb'] . "\n"; + } + + // Add headers and content when we have a body + if (isset($opt['body'])) + { + $request->set_body($opt['body']); + $headers['Content-Length'] = strlen($opt['body']); + + if ($headers['Content-Type'] === 'application/x-www-form-urlencoded') + { + $headers['Content-Type'] = 'application/octet-stream'; + } + + if (!isset($opt['NoContentMD5']) || $opt['NoContentMD5'] !== true) + { + $headers['Content-MD5'] = $this->util->hex_to_base64(md5($opt['body'])); + } + } + + // Handle query-string authentication + if (isset($opt['preauth']) && (integer) $opt['preauth'] > 0) + { + unset($headers['Date']); + $headers['Content-Type'] = ''; + $headers['Expires'] = is_int($opt['preauth']) ? $opt['preauth'] : strtotime($opt['preauth']); + } + + // Sort headers + uksort($headers, 'strnatcasecmp'); + + // Add headers to request and compute the string to sign + foreach ($headers as $header_key => $header_value) + { + // Strip linebreaks from header values as they're illegal and can allow for security issues + $header_value = str_replace(array("\r", "\n"), '', $header_value); + + // Add the header if it has a value + if ($header_value !== '') + { + $request->add_header($header_key, $header_value); + } + + // Generate the string to sign + if ( + strtolower($header_key) === 'content-md5' || + strtolower($header_key) === 'content-type' || + strtolower($header_key) === 'date' || + (strtolower($header_key) === 'expires' && isset($opt['preauth']) && (integer) $opt['preauth'] > 0) + ) + { + $string_to_sign .= $header_value . "\n"; + } + elseif (substr(strtolower($header_key), 0, 6) === 'x-amz-') + { + $string_to_sign .= strtolower($header_key) . ':' . $header_value . "\n"; + } + } + + // Add the signable resource location + $string_to_sign .= ($this->resource_prefix ? $this->resource_prefix : ''); + $string_to_sign .= (($bucket === '' || $this->resource_prefix === '/' . $bucket) ? '' : ('/' . $bucket)) . $signable_resource . urldecode($signable_query_string); + + // Hash the AWS secret key and generate a signature for the request. + $signature = base64_encode(hash_hmac('sha1', $string_to_sign, $this->secret_key, true)); + $request->add_header('Authorization', 'AWS ' . $this->key . ':' . $signature); + + // If we're generating a URL, return the URL to the calling method. + if (isset($opt['preauth']) && (integer) $opt['preauth'] > 0) + { + $query_params = array( + 'AWSAccessKeyId' => $this->key, + 'Expires' => $headers['Expires'], + 'Signature' => $signature, + ); + + // If using short-term credentials, add the token to the query string + if ($this->auth_token) + { + $query_params['x-amz-security-token'] = $this->auth_token; + } + + return $this->request_url . $conjunction . http_build_query($query_params, '', '&'); + } + elseif (isset($opt['preauth'])) + { + return $this->request_url; + } + + /*%******************************************************************************************%*/ + + // If our changes were temporary, reset them. + if ($this->temporary_prefix) + { + $this->temporary_prefix = false; + $this->resource_prefix = null; + } + + // Manage the (newer) batch request API or the (older) returnCurlHandle setting. + if ($this->use_batch_flow) + { + $handle = $request->prep_request(); + $this->batch_object->add($handle); + $this->use_batch_flow = false; + + return $handle; + } + elseif (isset($opt['returnCurlHandle']) && $opt['returnCurlHandle'] === true) + { + return $request->prep_request(); + } + + // Send! + $request->send_request(); + + // Prepare the response + $headers = $request->get_response_header(); + $headers['x-aws-request-url'] = $this->request_url; + $headers['x-aws-redirects'] = $this->redirects; + $headers['x-aws-stringtosign'] = $string_to_sign; + $headers['x-aws-requestheaders'] = $request->request_headers; + + // Did we have a request body? + if (isset($opt['body'])) + { + $headers['x-aws-requestbody'] = $opt['body']; + } + + $data = new $this->response_class($headers, $this->parse_callback($request->get_response_body()), $request->get_response_code()); + + // Did Amazon tell us to redirect? Typically happens for multiple rapid requests EU datacenters. + // @see: http://docs.amazonwebservices.com/AmazonS3/latest/dev/Redirects.html + // @codeCoverageIgnoreStart + if ((integer) $request->get_response_code() === 307) // Temporary redirect to new endpoint. + { + $this->redirects++; + $opt['location'] = $headers['location']; + $data = $this->authenticate($bucket, $opt); + } + + // Was it Amazon's fault the request failed? Retry the request until we reach $max_retries. + elseif ((integer) $request->get_response_code() === 500 || (integer) $request->get_response_code() === 503) + { + if ($this->redirects <= $this->max_retries) + { + // Exponential backoff + $delay = (integer) (pow(4, $this->redirects) * 100000); + usleep($delay); + $this->redirects++; + $data = $this->authenticate($bucket, $opt); + } + } + // @codeCoverageIgnoreEnd + + // Return! + $this->redirects = 0; + return $data; + } + + /** + * Validates whether or not the specified Amazon S3 bucket name is valid for DNS-style access. This + * method is leveraged by any method that creates buckets. + * + * @param string $bucket (Required) The name of the bucket to validate. + * @return boolean Whether or not the specified Amazon S3 bucket name is valid for DNS-style access. A value of true means that the bucket name is valid. A value of false means that the bucket name is invalid. + */ + public function validate_bucketname_create($bucket) + { + // list_buckets() uses this. Let it pass. + if ($bucket === '') return true; + + if ( + ($bucket === null || $bucket === false) || // Must not be null or false + preg_match('/[^(a-z0-9\-\.)]/', $bucket) || // Must be in the lowercase Roman alphabet, period or hyphen + !preg_match('/^([a-z]|\d)/', $bucket) || // Must start with a number or letter + !(strlen($bucket) >= 3 && strlen($bucket) <= 63) || // Must be between 3 and 63 characters long + (strpos($bucket, '..') !== false) || // Bucket names cannot contain two, adjacent periods + (strpos($bucket, '-.') !== false) || // Bucket names cannot contain dashes next to periods + (strpos($bucket, '.-') !== false) || // Bucket names cannot contain dashes next to periods + preg_match('/(-|\.)$/', $bucket) || // Bucket names should not end with a dash or period + preg_match('/^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$/', $bucket) // Must not be formatted as an IP address + ) return false; + + return true; + } + + /** + * Validates whether or not the specified Amazon S3 bucket name is valid for path-style access. This + * method is leveraged by any method that reads from buckets. + * + * @param string $bucket (Required) The name of the bucket to validate. + * @return boolean Whether or not the bucket name is valid. A value of true means that the bucket name is valid. A value of false means that the bucket name is invalid. + */ + public function validate_bucketname_support($bucket) + { + // list_buckets() uses this. Let it pass. + if ($bucket === '') return true; + + // Validate + if ( + ($bucket === null || $bucket === false) || // Must not be null or false + preg_match('/[^(a-z0-9_\-\.)]/i', $bucket) || // Must be in the Roman alphabet, period, hyphen or underscore + !preg_match('/^([a-z]|\d)/i', $bucket) || // Must start with a number or letter + !(strlen($bucket) >= 3 && strlen($bucket) <= 255) || // Must be between 3 and 255 characters long + preg_match('/^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$/', $bucket) // Must not be formatted as an IP address + ) return false; + + return true; + } + + /*%******************************************************************************************%*/ + // SETTERS + + /** + * Sets the region to use for subsequent Amazon S3 operations. This will also reset any prior use of + * . + * + * @param string $region (Required) The region to use for subsequent Amazon S3 operations. For a complete list of REGION constants, see the AmazonS3 Constants page in the API reference. + * @return $this A reference to the current instance. + */ + public function set_region($region) + { + // @codeCoverageIgnoreStart + $this->set_hostname($region); + + switch ($region) + { + case self::REGION_US_E1: // Northern Virginia + $this->enable_path_style(false); + break; + + case self::REGION_EU_W1: // Ireland + $this->enable_path_style(); // Always use path-style access for EU endpoint. + break; + + default: + $this->enable_path_style(false); + break; + + } + // @codeCoverageIgnoreEnd + + return $this; + } + + /** + * Sets the virtual host to use in place of the default `bucket.s3.amazonaws.com` domain. + * + * @param string $vhost (Required) The virtual host to use in place of the default `bucket.s3.amazonaws.com` domain. + * @return $this A reference to the current instance. + * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/VirtualHosting.html Virtual Hosting of Buckets + */ + public function set_vhost($vhost) + { + $this->vhost = $vhost; + return $this; + } + + /** + * Enables the use of the older path-style URI access for all requests. + * + * @param string $style (Optional) Whether or not to enable path-style URI access for all requests. The default value is true. + * @return $this A reference to the current instance. + */ + public function enable_path_style($style = true) + { + $this->path_style = $style; + return $this; + } + + + /*%******************************************************************************************%*/ + // BUCKET METHODS + + /** + * Creates an Amazon S3 bucket. + * + * Every object stored in Amazon S3 is contained in a bucket. Buckets partition the namespace of + * objects stored in Amazon S3 at the top level. in a bucket, any name can be used for objects. + * However, bucket names must be unique across all of Amazon S3. + * + * @param string $bucket (Required) The name of the bucket to create. + * @param string $region (Required) The preferred geographical location for the bucket. [Allowed values: `AmazonS3::REGION_US_E1 `, `AmazonS3::REGION_US_W1`, `AmazonS3::REGION_EU_W1`, `AmazonS3::REGION_APAC_SE1`, `AmazonS3::REGION_APAC_NE1`] + * @param string $acl (Optional) The ACL settings for the specified bucket. [Allowed values: AmazonS3::ACL_PRIVATE, AmazonS3::ACL_PUBLIC, AmazonS3::ACL_OPEN, AmazonS3::ACL_AUTH_READ, AmazonS3::ACL_OWNER_READ, AmazonS3::ACL_OWNER_FULL_CONTROL]. The default value is . + * @param array $opt (Optional) An associative array of parameters that can have the following keys:
    + *
  • curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
  • + *
  • returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request.
+ * @return CFResponse A object containing a parsed HTTP response. + * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/UsingBucket.html Working with Amazon S3 Buckets + */ + public function create_bucket($bucket, $region, $acl = self::ACL_PRIVATE, $opt = null) + { + // If the bucket contains uppercase letters... + if (preg_match('/[A-Z]/', $bucket)) + { + // Throw a warning + trigger_error('Since DNS-valid bucket names cannot contain uppercase characters, "' . $bucket . '" has been automatically converted to "' . strtolower($bucket) . '"', E_USER_WARNING); + + // Force the bucketname to lowercase + $bucket = strtolower($bucket); + } + + // Validate the S3 bucket name for creation + if (!$this->validate_bucketname_create($bucket)) + { + // @codeCoverageIgnoreStart + throw new S3_Exception('"' . $bucket . '" is not DNS-valid (i.e., .s3.amazonaws.com), and cannot be used as an S3 bucket name. Review "Bucket Restrictions and Limitations" in the S3 Developer Guide for more information.'); + // @codeCoverageIgnoreEnd + } + + if (!$opt) $opt = array(); + $opt['verb'] = 'PUT'; + $opt['headers'] = array( + 'Content-Type' => 'application/xml', + 'x-amz-acl' => $acl + ); + + // Defaults + $this->set_region($region); // Also sets path-style + $xml = simplexml_load_string($this->base_location_constraint); + + switch ($region) + { + case self::REGION_US_E1: // Northern Virginia + $opt['body'] = ''; + break; + + case self::REGION_EU_W1: // Ireland + $xml->LocationConstraint = 'EU'; + $opt['body'] = $xml->asXML(); + break; + + default: + $xml->LocationConstraint = str_replace(array('s3-', '.amazonaws.com'), '', $region); + $opt['body'] = $xml->asXML(); + break; + } + + $response = $this->authenticate($bucket, $opt); + + // Make sure we're set back to DNS-style URLs + $this->enable_path_style(false); + + return $response; + } + + /** + * Gets the region in which the specified Amazon S3 bucket is located. + * + * @param string $bucket (Required) The name of the bucket to use. + * @param array $opt (Optional) An associative array of parameters that can have the following keys:
    + *
  • preauth - integer|string - Optional - Specifies that a presigned URL for this request should be returned. May be passed as a number of seconds since UNIX Epoch, or any string compatible with .
  • + *
  • returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
+ * @return CFResponse A object containing a parsed HTTP response. + */ + public function get_bucket_region($bucket, $opt = null) + { + // Add this to our request + if (!$opt) $opt = array(); + $opt['verb'] = 'GET'; + $opt['sub_resource'] = 'location'; + + // Authenticate to S3 + $response = $this->authenticate($bucket, $opt); + + if ($response->isOK()) + { + // Handle body + $response->body = (string) $response->body; + } + + return $response; + } + + /** + * Gets the HTTP headers for the specified Amazon S3 bucket. + * + * @param string $bucket (Required) The name of the bucket to use. + * @param array $opt (Optional) An associative array of parameters that can have the following keys:
    + *
  • preauth - integer|string - Optional - Specifies that a presigned URL for this request should be returned. May be passed as a number of seconds since UNIX Epoch, or any string compatible with .
  • + *
  • curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
  • + *
  • returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
+ * @return CFResponse A object containing a parsed HTTP response. + */ + public function get_bucket_headers($bucket, $opt = null) + { + if (!$opt) $opt = array(); + $opt['verb'] = 'HEAD'; + + return $this->authenticate($bucket, $opt); + } + + /** + * Deletes a bucket from an Amazon S3 account. A bucket must be empty before the bucket itself can be deleted. + * + * @param string $bucket (Required) The name of the bucket to use. + * @param boolean $force (Optional) Whether to force-delete the bucket and all of its contents. The default value is false. + * @param array $opt (Optional) An associative array of parameters that can have the following keys:
    + *
  • curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
  • + *
  • returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
+ * @return mixed A object if the bucket was deleted successfully. Returns boolean false if otherwise. + */ + public function delete_bucket($bucket, $force = false, $opt = null) + { + // Set default value + $success = true; + + if ($force) + { + // Delete all of the items from the bucket. + $success = $this->delete_all_object_versions($bucket); + } + + // As long as we were successful... + if ($success) + { + if (!$opt) $opt = array(); + $opt['verb'] = 'DELETE'; + + return $this->authenticate($bucket, $opt); + } + + // @codeCoverageIgnoreStart + return false; + // @codeCoverageIgnoreEnd + } + + /** + * Gets a list of all buckets contained in the caller's Amazon S3 account. + * + * @param array $opt (Optional) An associative array of parameters that can have the following keys:
    + *
  • preauth - integer|string - Optional - Specifies that a presigned URL for this request should be returned. May be passed as a number of seconds since UNIX Epoch, or any string compatible with .
  • + *
  • curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
  • + *
  • returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
+ * @return CFResponse A object containing a parsed HTTP response. + */ + public function list_buckets($opt = null) + { + if (!$opt) $opt = array(); + $opt['verb'] = 'GET'; + + return $this->authenticate('', $opt); + } + + /** + * Gets the access control list (ACL) settings for the specified Amazon S3 bucket. + * + * @param string $bucket (Required) The name of the bucket to use. + * @param array $opt (Optional) An associative array of parameters that can have the following keys:
    + *
  • preauth - integer|string - Optional - Specifies that a presigned URL for this request should be returned. May be passed as a number of seconds since UNIX Epoch, or any string compatible with .
  • + *
  • curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
  • + *
  • returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
+ * @return CFResponse A object containing a parsed HTTP response. + * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAccessPolicy.html REST Access Control Policy + */ + public function get_bucket_acl($bucket, $opt = null) + { + // Add this to our request + if (!$opt) $opt = array(); + $opt['verb'] = 'GET'; + $opt['sub_resource'] = 'acl'; + + // Authenticate to S3 + return $this->authenticate($bucket, $opt); + } + + /** + * Sets the access control list (ACL) settings for the specified Amazon S3 bucket. + * + * @param string $bucket (Required) The name of the bucket to use. + * @param string $acl (Optional) The ACL settings for the specified bucket. [Allowed values: AmazonS3::ACL_PRIVATE, AmazonS3::ACL_PUBLIC, AmazonS3::ACL_OPEN, AmazonS3::ACL_AUTH_READ, AmazonS3::ACL_OWNER_READ, AmazonS3::ACL_OWNER_FULL_CONTROL]. Alternatively, an array of associative arrays. Each associative array contains an `id` and a `permission` key. The default value is . + * @param array $opt (Optional) An associative array of parameters that can have the following keys:
    + *
  • curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
  • + *
  • returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
+ * @return CFResponse A object containing a parsed HTTP response. + * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAccessPolicy.html REST Access Control Policy + */ + public function set_bucket_acl($bucket, $acl = self::ACL_PRIVATE, $opt = null) + { + // Add this to our request + if (!$opt) $opt = array(); + $opt['verb'] = 'PUT'; + $opt['sub_resource'] = 'acl'; + $opt['headers'] = array( + 'Content-Type' => 'application/xml' + ); + + // Make sure these are defined. + // @codeCoverageIgnoreStart + if (!$this->credentials->canonical_id || !$this->credentials->canonical_name) + { + // Fetch the data live. + $canonical = $this->get_canonical_user_id(); + $this->credentials->canonical_id = $canonical['id']; + $this->credentials->canonical_name = $canonical['display_name']; + } + // @codeCoverageIgnoreEnd + + if (is_array($acl)) + { + $opt['body'] = $this->generate_access_policy($this->credentials->canonical_id, $this->credentials->canonical_name, $acl); + } + else + { + $opt['body'] = ''; + $opt['headers']['x-amz-acl'] = $acl; + } + + // Authenticate to S3 + return $this->authenticate($bucket, $opt); + } + + + /*%******************************************************************************************%*/ + // OBJECT METHODS + + /** + * Creates an Amazon S3 object. After an Amazon S3 bucket is created, objects can be stored in it. + * + * Each standard object can hold up to 5 GB of data. When an object is stored in Amazon S3, the data is streamed + * to multiple storage servers in multiple data centers. This ensures the data remains available in the + * event of internal network or hardware failure. + * + * @param string $bucket (Required) The name of the bucket to use. + * @param string $filename (Required) The file name for the object. + * @param array $opt (Optional) An associative array of parameters that can have the following keys:
    + *
  • body - string - Required; Conditional - The data to be stored in the object. Either this parameter or fileUpload must be specified.
  • + *
  • fileUpload - string|resource - Required; Conditional - The URL/path for the file to upload, or an open resource. Either this parameter or body is required.
  • + *
  • acl - string - Optional - The ACL settings for the specified object. [Allowed values: AmazonS3::ACL_PRIVATE, AmazonS3::ACL_PUBLIC, AmazonS3::ACL_OPEN, AmazonS3::ACL_AUTH_READ, AmazonS3::ACL_OWNER_READ, AmazonS3::ACL_OWNER_FULL_CONTROL]. The default value is ACL_PRIVATE.
  • + *
  • contentType - string - Optional - The type of content that is being sent in the body. If a file is being uploaded via fileUpload as a file system path, it will attempt to determine the correct mime-type based on the file extension. The default value is application/octet-stream.
  • + *
  • encryption - string - Optional - The algorithm to use for encrypting the object. [Allowed values: AES256]
  • + *
  • headers - array - Optional - Standard HTTP headers to send along in the request. Accepts an associative array of key-value pairs.
  • + *
  • length - integer - Optional - The size of the object in bytes. For more information, see RFC 2616, section 14.13. The value can also be passed to the header option as Content-Length.
  • + *
  • meta - array - Optional - An associative array of key-value pairs. Represented by x-amz-meta-:. Any header starting with this prefix is considered user metadata. It will be stored with the object and returned when you retrieve the object. The total size of the HTTP request, not including the body, must be less than 4 KB.
  • + *
  • seekTo - integer - Optional - The starting position in bytes within the file/stream to upload from.
  • + *
  • storage - string - Optional - Whether to use Standard or Reduced Redundancy storage. [Allowed values: AmazonS3::STORAGE_STANDARD, AmazonS3::STORAGE_REDUCED]. The default value is STORAGE_STANDARD.
  • + *
  • curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
  • + *
  • returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
+ * @return CFResponse A object containing a parsed HTTP response. + * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAccessPolicy.html REST Access Control Policy + */ + public function create_object($bucket, $filename, $opt = null) + { + if (!$opt) $opt = array(); + + // Add this to our request + $opt['verb'] = 'PUT'; + $opt['resource'] = $filename; + + // Handle content length. Can also be passed as an HTTP header. + if (isset($opt['length'])) + { + $opt['headers']['Content-Length'] = $opt['length']; + unset($opt['length']); + } + + // Handle content type. Can also be passed as an HTTP header. + if (isset($opt['contentType'])) + { + $opt['headers']['Content-Type'] = $opt['contentType']; + unset($opt['contentType']); + } + + // Handle Access Control Lists. Can also be passed as an HTTP header. + if (isset($opt['acl'])) + { + $opt['headers']['x-amz-acl'] = $opt['acl']; + unset($opt['acl']); + } + + // Handle storage settings. Can also be passed as an HTTP header. + if (isset($opt['storage'])) + { + $opt['headers']['x-amz-storage-class'] = $opt['storage']; + unset($opt['storage']); + } + + // Handle encryption settings. Can also be passed as an HTTP header. + if (isset($opt['encryption'])) + { + $opt['headers']['x-amz-server-side-encryption'] = $opt['encryption']; + unset($opt['encryption']); + } + + // Handle meta tags. Can also be passed as an HTTP header. + if (isset($opt['meta'])) + { + foreach ($opt['meta'] as $meta_key => $meta_value) + { + // e.g., `My Meta Header` is converted to `x-amz-meta-my-meta-header`. + $opt['headers']['x-amz-meta-' . strtolower(str_replace(' ', '-', $meta_key))] = $meta_value; + } + unset($opt['meta']); + } + + $opt['headers']['Expect'] = '100-continue'; + + // Authenticate to S3 + return $this->authenticate($bucket, $opt); + } + + /** + * Gets the contents of an Amazon S3 object in the specified bucket. + * + * @param string $bucket (Required) The name of the bucket to use. + * @param string $filename (Required) The file name for the object. + * @param array $opt (Optional) An associative array of parameters that can have the following keys:
    + *
  • etag - string - Optional - The ETag header passed in from a previous request. If specified, request LastModified option must be specified as well. Will trigger a 304 Not Modified status code if the file hasn't changed.
  • + *
  • fileDownload - string|resource - Optional - The file system location to download the file to, or an open file resource. Must be a server-writable location.
  • + *
  • headers - array - Optional - Standard HTTP headers to send along in the request. Accepts an associative array of key-value pairs.
  • + *
  • lastmodified - string - Optional - The LastModified header passed in from a previous request. If specified, request ETag option must be specified as well. Will trigger a 304 Not Modified status code if the file hasn't changed.
  • + *
  • preauth - integer|string - Optional - Specifies that a presigned URL for this request should be returned. May be passed as a number of seconds since UNIX Epoch, or any string compatible with .
  • + *
  • range - string - Optional - The range of bytes to fetch from the object. Specify this parameter when downloading partial bits or completing incomplete object downloads. The specified range must be notated with a hyphen (e.g., 0-10485759). Defaults to the byte range of the complete Amazon S3 object.
  • + *
  • response - array - Optional - Allows adjustments to specific response headers. Pass an associative array where each key is one of the following: cache-control, content-disposition, content-encoding, content-language, content-type, expires. The expires value should use and be formatted with the DATE_RFC2822 constant.
  • + *
  • versionId - string - Optional - The version of the object to retrieve. Version IDs are returned in the x-amz-version-id header of any previous object-related request.
  • + *
  • curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
  • + *
  • returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
+ * @return CFResponse A object containing a parsed HTTP response. + */ + public function get_object($bucket, $filename, $opt = null) + { + if (!$opt) $opt = array(); + + // Add this to our request + $opt['verb'] = 'GET'; + $opt['resource'] = $filename; + + if (!isset($opt['headers']) || !is_array($opt['headers'])) + { + $opt['headers'] = array(); + } + + if (isset($opt['lastmodified'])) + { + $opt['headers']['If-Modified-Since'] = $opt['lastmodified']; + } + + if (isset($opt['etag'])) + { + $opt['headers']['If-None-Match'] = $opt['etag']; + } + + // Partial content range + if (isset($opt['range'])) + { + $opt['headers']['Range'] = 'bytes=' . $opt['range']; + } + + // GET responses + if (isset($opt['response'])) + { + foreach ($opt['response'] as $key => $value) + { + $opt['response-' . $key] = $value; + unset($opt['response'][$key]); + } + } + + // Authenticate to S3 + $this->parse_the_response = false; + $response = $this->authenticate($bucket, $opt); + $this->parse_the_response = true; + + return $response; + } + + /** + * Gets the HTTP headers for the specified Amazon S3 object. + * + * @param string $bucket (Required) The name of the bucket to use. + * @param string $filename (Required) The file name for the object. + * @param array $opt (Optional) An associative array of parameters that can have the following keys:
    + *
  • versionId - string - Optional - The version of the object to retrieve. Version IDs are returned in the x-amz-version-id header of any previous object-related request.
  • + *
  • preauth - integer|string - Optional - Specifies that a presigned URL for this request should be returned. May be passed as a number of seconds since UNIX Epoch, or any string compatible with .
  • + *
  • curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
  • + *
  • returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
+ * @return CFResponse A object containing a parsed HTTP response. + */ + public function get_object_headers($bucket, $filename, $opt = null) + { + // Add this to our request + if (!$opt) $opt = array(); + $opt['verb'] = 'HEAD'; + $opt['resource'] = $filename; + + // Authenticate to S3 + return $this->authenticate($bucket, $opt); + } + + /** + * Deletes an Amazon S3 object from the specified bucket. + * + * @param string $bucket (Required) The name of the bucket to use. + * @param string $filename (Required) The file name for the object. + * @param array $opt (Optional) An associative array of parameters that can have the following keys:
    + *
  • versionId - string - Optional - The version of the object to delete. Version IDs are returned in the x-amz-version-id header of any previous object-related request.
  • + *
  • MFASerial - string - Optional - The serial number on the back of the Gemalto device. MFASerial and MFAToken must both be set for MFA to work.
  • + *
  • MFAToken - string - Optional - The current token displayed on the Gemalto device. MFASerial and MFAToken must both be set for MFA to work.
  • + *
  • curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
  • + *
  • returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
+ * @return CFResponse A object containing a parsed HTTP response. + * @link http://aws.amazon.com/mfa/ Multi-Factor Authentication + */ + public function delete_object($bucket, $filename, $opt = null) + { + // Add this to our request + if (!$opt) $opt = array(); + $opt['verb'] = 'DELETE'; + $opt['resource'] = $filename; + + // Enable MFA delete? + // @codeCoverageIgnoreStart + if (isset($opt['MFASerial']) && isset($opt['MFAToken'])) + { + $opt['headers'] = array( + 'x-amz-mfa' => ($opt['MFASerial'] . ' ' . $opt['MFAToken']) + ); + } + // @codeCoverageIgnoreEnd + + // Authenticate to S3 + return $this->authenticate($bucket, $opt); + } + + /** + * Deletes two or more specified Amazon S3 objects from the specified bucket. + * + * @param string $bucket (Required) The name of the bucket to use. + * @param array $opt (Optional) An associative array of parameters that can have the following keys:
    + *
  • objects - array - Required - The object references to delete from the bucket.
      + *
    • key - string - Required - The name of the object (e.g., the "key") to delete. This should include the entire file path including all "subdirectories".
    • + *
    • version_id - string - Optional - If the object is versioned, include the version ID to delete.
    • + *
  • + *
  • quiet - boolean - Optional - Whether or not Amazon S3 should use "Quiet" mode for this operation. A value of true will enable Quiet mode. A value of false will use Verbose mode. The default value is false.
  • + *
  • MFASerial - string - Optional - The serial number on the back of the Gemalto device. MFASerial and MFAToken must both be set for MFA to work.
  • + *
  • MFAToken - string - Optional - The current token displayed on the Gemalto device. MFASerial and MFAToken must both be set for MFA to work.
  • + *
  • curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
  • + *
  • returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
+ * @return CFResponse A object containing a parsed HTTP response. + * @link http://aws.amazon.com/mfa/ Multi-Factor Authentication + */ + public function delete_objects($bucket, $opt = null) + { + // Add this to our request + if (!$opt) $opt = array(); + $opt['verb'] = 'POST'; + $opt['sub_resource'] = 'delete'; + $opt['body'] = ''; + + // Bail out + if (!isset($opt['objects']) || !is_array($opt['objects'])) + { + throw new S3_Exception('The ' . __FUNCTION__ . ' method requires the "objects" option to be set as an array.'); + } + + $xml = new SimpleXMLElement($this->multi_object_delete_xml); + + // Add the objects + foreach ($opt['objects'] as $object) + { + $xobject = $xml->addChild('Object'); + $xobject->addChild('Key', $object['key']); + + if (isset($object['version_id'])) + { + $xobject->addChild('VersionId', $object['version_id']); + } + } + + // Quiet mode? + if (isset($opt['quiet'])) + { + $quiet = 'false'; + if (is_bool($opt['quiet'])) // Boolean + { + $quiet = $opt['quiet'] ? 'true' : 'false'; + } + elseif (is_string($opt['quiet'])) // String + { + $quiet = ($opt['quiet'] === 'true') ? 'true' : 'false'; + } + + $xml->addChild('Quiet', $quiet); + } + + // Enable MFA delete? + // @codeCoverageIgnoreStart + if (isset($opt['MFASerial']) && isset($opt['MFAToken'])) + { + $opt['headers'] = array( + 'x-amz-mfa' => ($opt['MFASerial'] . ' ' . $opt['MFAToken']) + ); + } + // @codeCoverageIgnoreEnd + + $opt['body'] = $xml->asXML(); + + // Authenticate to S3 + return $this->authenticate($bucket, $opt); + } + + /** + * Gets a list of all Amazon S3 objects in the specified bucket. + * + * NOTE: This method is paginated, and will not return more than max-keys keys. If you want to retrieve a list of all keys, you will need to make multiple calls to this function using the marker option to specify the pagination offset (the key of the last processed key--lexically ordered) and the IsTruncated response key to detect when all results have been processed. See: the S3 REST documentation for get_bucket for more information. + * + * @param string $bucket (Required) The name of the bucket to use. + * @param array $opt (Optional) An associative array of parameters that can have the following keys:
    + *
  • delimiter - string - Optional - Keys that contain the same string between the prefix and the first occurrence of the delimiter will be rolled up into a single result element in the CommonPrefixes collection.
  • + *
  • marker - string - Optional - Restricts the response to contain results that only occur alphabetically after the value of the marker.
  • + *
  • max-keys - string - Optional - The maximum number of results returned by the method call. The returned list will contain no more results than the specified value, but may return fewer. The default value is 1000.
  • + *
  • preauth - integer|string - Optional - Specifies that a presigned URL for this request should be returned. May be passed as a number of seconds since UNIX Epoch, or any string compatible with .
  • + *
  • prefix - string - Optional - Restricts the response to contain results that begin only with the specified prefix.
  • + *
  • curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
  • + *
  • returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
+ * @return CFResponse A object containing a parsed HTTP response. + */ + public function list_objects($bucket, $opt = null) + { + if (!$opt) $opt = array(); + + // Add this to our request + $opt['verb'] = 'GET'; + + foreach (array('delimiter', 'marker', 'max-keys', 'prefix') as $param) + { + if (isset($opt[$param])) + { + $opt['query_string'][$param] = $opt[$param]; + unset($opt[$param]); + } + } + + // Authenticate to S3 + return $this->authenticate($bucket, $opt); + } + + /** + * Copies an Amazon S3 object to a new location, whether in the same Amazon S3 region, bucket, or otherwise. + * + * @param array $source (Required) The bucket and file name to copy from. The following keys must be set:
    + *
  • bucket - string - Required - Specifies the name of the bucket containing the source object.
  • + *
  • filename - string - Required - Specifies the file name of the source object to copy.
+ * @param array $dest (Required) The bucket and file name to copy to. The following keys must be set:
    + *
  • bucket - string - Required - Specifies the name of the bucket to copy the object to.
  • + *
  • filename - string - Required - Specifies the file name to copy the object to.
+ * @param array $opt (Optional) An associative array of parameters that can have the following keys:
    + *
  • acl - string - Optional - The ACL settings for the specified object. [Allowed values: AmazonS3::ACL_PRIVATE, AmazonS3::ACL_PUBLIC, AmazonS3::ACL_OPEN, AmazonS3::ACL_AUTH_READ, AmazonS3::ACL_OWNER_READ, AmazonS3::ACL_OWNER_FULL_CONTROL]. Alternatively, an array of associative arrays. Each associative array contains an id and a permission key. The default value is ACL_PRIVATE.
  • + *
  • encryption - string - Optional - The algorithm to use for encrypting the object. [Allowed values: AES256]
  • + *
  • storage - string - Optional - Whether to use Standard or Reduced Redundancy storage. [Allowed values: AmazonS3::STORAGE_STANDARD, AmazonS3::STORAGE_REDUCED]. The default value is STORAGE_STANDARD.
  • + *
  • versionId - string - Optional - The version of the object to copy. Version IDs are returned in the x-amz-version-id header of any previous object-related request.
  • + *
  • ifMatch - string - Optional - The ETag header from a previous request. Copies the object if its entity tag (ETag) matches the specified tag; otherwise, the request returns a 412 HTTP status code error (precondition failed). Used in conjunction with ifUnmodifiedSince.
  • + *
  • ifUnmodifiedSince - string - Optional - The LastModified header from a previous request. Copies the object if it hasn't been modified since the specified time; otherwise, the request returns a 412 HTTP status code error (precondition failed). Used in conjunction with ifMatch.
  • + *
  • ifNoneMatch - string - Optional - The ETag header from a previous request. Copies the object if its entity tag (ETag) is different than the specified ETag; otherwise, the request returns a 412 HTTP status code error (failed condition). Used in conjunction with ifModifiedSince.
  • + *
  • ifModifiedSince - string - Optional - The LastModified header from a previous request. Copies the object if it has been modified since the specified time; otherwise, the request returns a 412 HTTP status code error (failed condition). Used in conjunction with ifNoneMatch.
  • + *
  • headers - array - Optional - Standard HTTP headers to send along in the request. Accepts an associative array of key-value pairs.
  • + *
  • meta - array - Optional - Associative array of key-value pairs. Represented by x-amz-meta-: Any header starting with this prefix is considered user metadata. It will be stored with the object and returned when you retrieve the object. The total size of the HTTP request, not including the body, must be less than 4 KB.
  • + *
  • metadataDirective - string - Optional - Accepts either COPY or REPLACE. You will likely never need to use this, as it manages itself with no issues.
  • + *
  • curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
  • + *
  • returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
+ * @return CFResponse A object containing a parsed HTTP response. + * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/API/RESTObjectCOPY.html Copying Amazon S3 Objects + */ + public function copy_object($source, $dest, $opt = null) + { + if (!$opt) $opt = array(); + $batch = array(); + + // Add this to our request + $opt['verb'] = 'PUT'; + $opt['resource'] = $dest['filename']; + $opt['body'] = ''; + + // Handle copy source + if (isset($source['bucket']) && isset($source['filename'])) + { + $opt['headers']['x-amz-copy-source'] = '/' . $source['bucket'] . '/' . rawurlencode($source['filename']) + . (isset($opt['versionId']) ? ('?' . 'versionId=' . rawurlencode($opt['versionId'])) : ''); // Append the versionId to copy, if available + unset($opt['versionId']); + + // Determine if we need to lookup the pre-existing content-type. + if ( + (!$this->use_batch_flow && !isset($opt['returnCurlHandle'])) && + !in_array(strtolower('content-type'), array_map('strtolower', array_keys($opt['headers']))) + ) + { + $response = $this->get_object_headers($source['bucket'], $source['filename']); + if ($response->isOK()) + { + $opt['headers']['Content-Type'] = $response->header['content-type']; + } + } + } + + // Handle metadata directive + $opt['headers']['x-amz-metadata-directive'] = 'COPY'; + if ($source['bucket'] === $dest['bucket'] && $source['filename'] === $dest['filename']) + { + $opt['headers']['x-amz-metadata-directive'] = 'REPLACE'; + } + if (isset($opt['metadataDirective'])) + { + $opt['headers']['x-amz-metadata-directive'] = $opt['metadataDirective']; + unset($opt['metadataDirective']); + } + + // Handle Access Control Lists. Can also pass canned ACLs as an HTTP header. + if (isset($opt['acl']) && is_array($opt['acl'])) + { + $batch[] = $this->set_object_acl($dest['bucket'], $dest['filename'], $opt['acl'], array( + 'returnCurlHandle' => true + )); + unset($opt['acl']); + } + elseif (isset($opt['acl'])) + { + $opt['headers']['x-amz-acl'] = $opt['acl']; + unset($opt['acl']); + } + + // Handle storage settings. Can also be passed as an HTTP header. + if (isset($opt['storage'])) + { + $opt['headers']['x-amz-storage-class'] = $opt['storage']; + unset($opt['storage']); + } + + // Handle encryption settings. Can also be passed as an HTTP header. + if (isset($opt['encryption'])) + { + $opt['headers']['x-amz-server-side-encryption'] = $opt['encryption']; + unset($opt['encryption']); + } + + // Handle conditional-copy parameters + if (isset($opt['ifMatch'])) + { + $opt['headers']['x-amz-copy-source-if-match'] = $opt['ifMatch']; + unset($opt['ifMatch']); + } + if (isset($opt['ifNoneMatch'])) + { + $opt['headers']['x-amz-copy-source-if-none-match'] = $opt['ifNoneMatch']; + unset($opt['ifNoneMatch']); + } + if (isset($opt['ifUnmodifiedSince'])) + { + $opt['headers']['x-amz-copy-source-if-unmodified-since'] = $opt['ifUnmodifiedSince']; + unset($opt['ifUnmodifiedSince']); + } + if (isset($opt['ifModifiedSince'])) + { + $opt['headers']['x-amz-copy-source-if-modified-since'] = $opt['ifModifiedSince']; + unset($opt['ifModifiedSince']); + } + + // Handle meta tags. Can also be passed as an HTTP header. + if (isset($opt['meta'])) + { + foreach ($opt['meta'] as $meta_key => $meta_value) + { + // e.g., `My Meta Header` is converted to `x-amz-meta-my-meta-header`. + $opt['headers']['x-amz-meta-' . strtolower(str_replace(' ', '-', $meta_key))] = $meta_value; + } + unset($opt['meta']); + } + + // Authenticate to S3 + $response = $this->authenticate($dest['bucket'], $opt); + + // Attempt to reset ACLs + $http = new RequestCore(); + $http->send_multi_request($batch); + + return $response; + } + + /** + * Updates an Amazon S3 object with new headers or other metadata. To replace the content of the + * specified Amazon S3 object, call with the same bucket and file name parameters. + * + * @param string $bucket (Required) The name of the bucket that contains the source file. + * @param string $filename (Required) The source file name that you want to update. + * @param array $opt (Optional) An associative array of parameters that can have the following keys:
    + *
  • acl - string - Optional - The ACL settings for the specified object. [Allowed values: AmazonS3::ACL_PRIVATE, AmazonS3::ACL_PUBLIC, AmazonS3::ACL_OPEN, AmazonS3::ACL_AUTH_READ, AmazonS3::ACL_OWNER_READ, AmazonS3::ACL_OWNER_FULL_CONTROL]. The default value is .
  • + *
  • headers - array - Optional - Standard HTTP headers to send along in the request. Accepts an associative array of key-value pairs.
  • + *
  • meta - array - Optional - An associative array of key-value pairs. Any header with the x-amz-meta- prefix is considered user metadata and is stored with the Amazon S3 object. It will be stored with the object and returned when you retrieve the object. The total size of the HTTP request, not including the body, must be less than 4 KB.
  • + *
  • curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
  • + *
  • returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
+ * @return CFResponse A object containing a parsed HTTP response. + * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/API/RESTObjectCOPY.html Copying Amazon S3 Objects + */ + public function update_object($bucket, $filename, $opt = null) + { + if (!$opt) $opt = array(); + $opt['metadataDirective'] = 'REPLACE'; + + // Authenticate to S3 + return $this->copy_object( + array('bucket' => $bucket, 'filename' => $filename), + array('bucket' => $bucket, 'filename' => $filename), + $opt + ); + } + + + /*%******************************************************************************************%*/ + // ACCESS CONTROL LISTS + + /** + * Gets the access control list (ACL) settings for the specified Amazon S3 object. + * + * @param string $bucket (Required) The name of the bucket to use. + * @param string $filename (Required) The file name for the object. + * @param array $opt (Optional) An associative array of parameters that can have the following keys:
    + *
  • versionId - string - Optional - The version of the object to retrieve. Version IDs are returned in the x-amz-version-id header of any previous object-related request.
  • + *
  • preauth - integer|string - Optional - Specifies that a presigned URL for this request should be returned. May be passed as a number of seconds since UNIX Epoch, or any string compatible with .
  • + *
  • curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
  • + *
  • returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
+ * @return CFResponse A object containing a parsed HTTP response. + * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAccessPolicy.html REST Access Control Policy + */ + public function get_object_acl($bucket, $filename, $opt = null) + { + // Add this to our request + if (!$opt) $opt = array(); + $opt['verb'] = 'GET'; + $opt['resource'] = $filename; + $opt['sub_resource'] = 'acl'; + + // Authenticate to S3 + return $this->authenticate($bucket, $opt); + } + + /** + * Sets the access control list (ACL) settings for the specified Amazon S3 object. + * + * @param string $bucket (Required) The name of the bucket to use. + * @param string $filename (Required) The file name for the object. + * @param string $acl (Optional) The ACL settings for the specified object. Accepts any of the following constants: [Allowed values: AmazonS3::ACL_PRIVATE, AmazonS3::ACL_PUBLIC, AmazonS3::ACL_OPEN, AmazonS3::ACL_AUTH_READ, AmazonS3::ACL_OWNER_READ, AmazonS3::ACL_OWNER_FULL_CONTROL]. Alternatively, an array of associative arrays. Each associative array contains an id and a permission key. The default value is ACL_PRIVATE. + * @param array $opt (Optional) An associative array of parameters that can have the following keys:
    + *
  • curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
  • + *
  • returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
+ * @return CFResponse A object containing a parsed HTTP response. + * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAccessPolicy.html REST Access Control Policy + */ + public function set_object_acl($bucket, $filename, $acl = self::ACL_PRIVATE, $opt = null) + { + // Add this to our request + if (!$opt) $opt = array(); + $opt['verb'] = 'PUT'; + $opt['resource'] = $filename; + $opt['sub_resource'] = 'acl'; + + // Retrieve the original metadata + $metadata = $this->get_object_metadata($bucket, $filename); + if ($metadata && $metadata['ContentType']) + { + $opt['headers']['Content-Type'] = $metadata['ContentType']; + } + if ($metadata && $metadata['StorageClass']) + { + $opt['headers']['x-amz-storage-class'] = $metadata['StorageClass']; + } + + // Make sure these are defined. + // @codeCoverageIgnoreStart + if (!$this->credentials->canonical_id || !$this->credentials->canonical_name) + { + // Fetch the data live. + $canonical = $this->get_canonical_user_id(); + $this->credentials->canonical_id = $canonical['id']; + $this->credentials->canonical_name = $canonical['display_name']; + } + // @codeCoverageIgnoreEnd + + if (is_array($acl)) + { + $opt['body'] = $this->generate_access_policy($this->credentials->canonical_id, $this->credentials->canonical_name, $acl); + } + else + { + $opt['body'] = ''; + $opt['headers']['x-amz-acl'] = $acl; + } + + // Authenticate to S3 + return $this->authenticate($bucket, $opt); + } + + /** + * Generates the XML to be used for the Access Control Policy. + * + * @param string $canonical_id (Required) The canonical ID for the bucket owner. This is provided as the `id` return value from . + * @param string $canonical_name (Required) The canonical display name for the bucket owner. This is provided as the `display_name` value from . + * @param array $users (Optional) An array of associative arrays. Each associative array contains an `id` value and a `permission` value. + * @return string Access Control Policy XML. + * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/S3_ACLs.html Access Control Lists + */ + public function generate_access_policy($canonical_id, $canonical_name, $users) + { + $xml = simplexml_load_string($this->base_acp_xml); + $owner = $xml->addChild('Owner'); + $owner->addChild('ID', $canonical_id); + $owner->addChild('DisplayName', $canonical_name); + $acl = $xml->addChild('AccessControlList'); + + foreach ($users as $user) + { + $grant = $acl->addChild('Grant'); + $grantee = $grant->addChild('Grantee'); + + switch ($user['id']) + { + // Authorized Users + case self::USERS_AUTH: + $grantee->addAttribute('xsi:type', 'Group', 'http://www.w3.org/2001/XMLSchema-instance'); + $grantee->addChild('URI', self::USERS_AUTH); + break; + + // All Users + case self::USERS_ALL: + $grantee->addAttribute('xsi:type', 'Group', 'http://www.w3.org/2001/XMLSchema-instance'); + $grantee->addChild('URI', self::USERS_ALL); + break; + + // The Logging User + case self::USERS_LOGGING: + $grantee->addAttribute('xsi:type', 'Group', 'http://www.w3.org/2001/XMLSchema-instance'); + $grantee->addChild('URI', self::USERS_LOGGING); + break; + + // Email Address or Canonical Id + default: + if (strpos($user['id'], '@')) + { + $grantee->addAttribute('xsi:type', 'AmazonCustomerByEmail', 'http://www.w3.org/2001/XMLSchema-instance'); + $grantee->addChild('EmailAddress', $user['id']); + } + else + { + // Assume Canonical Id + $grantee->addAttribute('xsi:type', 'CanonicalUser', 'http://www.w3.org/2001/XMLSchema-instance'); + $grantee->addChild('ID', $user['id']); + } + break; + } + + $grant->addChild('Permission', $user['permission']); + } + + return $xml->asXML(); + } + + + /*%******************************************************************************************%*/ + // LOGGING METHODS + + /** + * Gets the access logs associated with the specified Amazon S3 bucket. + * + * @param string $bucket (Required) The name of the bucket to use. Pass a `null` value when using the method. + * @param array $opt (Optional) An associative array of parameters that can have the following keys:
    + *
  • preauth - integer|string - Optional - Specifies that a presigned URL for this request should be returned. May be passed as a number of seconds since UNIX Epoch, or any string compatible with .
  • + *
  • curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
  • + *
  • returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
+ * @return CFResponse A object containing a parsed HTTP response. + * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/ServerLogs.html Server Access Logging + */ + public function get_logs($bucket, $opt = null) + { + // Add this to our request + if (!$opt) $opt = array(); + $opt['verb'] = 'GET'; + $opt['sub_resource'] = 'logging'; + + // Authenticate to S3 + return $this->authenticate($bucket, $opt); + } + + /** + * Enables access logging for the specified Amazon S3 bucket. + * + * @param string $bucket (Required) The name of the bucket to enable logging for. Pass a `null` value when using the method. + * @param string $target_bucket (Required) The name of the bucket to store the logs in. + * @param string $target_prefix (Required) The prefix to give to the log file names. + * @param array $opt (Optional) An associative array of parameters that can have the following keys:
    + *
  • users - array - Optional - An array of associative arrays specifying any user to give access to. Each associative array contains an id and permission value.
  • + *
  • curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
  • + *
  • returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
+ * @return CFResponse A object containing a parsed HTTP response. + * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/LoggingAPI.html Server Access Logging Configuration API + */ + public function enable_logging($bucket, $target_bucket, $target_prefix, $opt = null) + { + // Add this to our request + if (!$opt) $opt = array(); + $opt['verb'] = 'PUT'; + $opt['sub_resource'] = 'logging'; + $opt['headers'] = array( + 'Content-Type' => 'application/xml' + ); + + $xml = simplexml_load_string($this->base_logging_xml); + $LoggingEnabled = $xml->addChild('LoggingEnabled'); + $LoggingEnabled->addChild('TargetBucket', $target_bucket); + $LoggingEnabled->addChild('TargetPrefix', $target_prefix); + $TargetGrants = $LoggingEnabled->addChild('TargetGrants'); + + if (isset($opt['users']) && is_array($opt['users'])) + { + foreach ($opt['users'] as $user) + { + $grant = $TargetGrants->addChild('Grant'); + $grantee = $grant->addChild('Grantee'); + + switch ($user['id']) + { + // Authorized Users + case self::USERS_AUTH: + $grantee->addAttribute('xsi:type', 'Group', 'http://www.w3.org/2001/XMLSchema-instance'); + $grantee->addChild('URI', self::USERS_AUTH); + break; + + // All Users + case self::USERS_ALL: + $grantee->addAttribute('xsi:type', 'Group', 'http://www.w3.org/2001/XMLSchema-instance'); + $grantee->addChild('URI', self::USERS_ALL); + break; + + // The Logging User + case self::USERS_LOGGING: + $grantee->addAttribute('xsi:type', 'Group', 'http://www.w3.org/2001/XMLSchema-instance'); + $grantee->addChild('URI', self::USERS_LOGGING); + break; + + // Email Address or Canonical Id + default: + if (strpos($user['id'], '@')) + { + $grantee->addAttribute('xsi:type', 'AmazonCustomerByEmail', 'http://www.w3.org/2001/XMLSchema-instance'); + $grantee->addChild('EmailAddress', $user['id']); + } + else + { + // Assume Canonical Id + $grantee->addAttribute('xsi:type', 'CanonicalUser', 'http://www.w3.org/2001/XMLSchema-instance'); + $grantee->addChild('ID', $user['id']); + } + break; + } + + $grant->addChild('Permission', $user['permission']); + } + } + + $opt['body'] = $xml->asXML(); + + // Authenticate to S3 + return $this->authenticate($bucket, $opt); + } + + /** + * Disables access logging for the specified Amazon S3 bucket. + * + * @param string $bucket (Required) The name of the bucket to use. Pass `null` if using . + * @param array $opt (Optional) An associative array of parameters that can have the following keys:
    + *
  • curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
  • + *
  • returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
+ * @return CFResponse A object containing a parsed HTTP response. + * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/LoggingAPI.html Server Access Logging Configuration API + */ + public function disable_logging($bucket, $opt = null) + { + // Add this to our request + if (!$opt) $opt = array(); + $opt['verb'] = 'PUT'; + $opt['sub_resource'] = 'logging'; + $opt['headers'] = array( + 'Content-Type' => 'application/xml' + ); + $opt['body'] = $this->base_logging_xml; + + // Authenticate to S3 + return $this->authenticate($bucket, $opt); + } + + + /*%******************************************************************************************%*/ + // CONVENIENCE METHODS + + /** + * Gets whether or not the specified Amazon S3 bucket exists in Amazon S3. This includes buckets + * that do not belong to the caller. + * + * @param string $bucket (Required) The name of the bucket to use. + * @return boolean A value of true if the bucket exists, or a value of false if it does not. + */ + public function if_bucket_exists($bucket) + { + if ($this->use_batch_flow) + { + throw new S3_Exception(__FUNCTION__ . '() cannot be batch requested'); + } + + $header = $this->get_bucket_headers($bucket); + return (bool) $header->isOK(); + } + + /** + * Gets whether or not the specified Amazon S3 object exists in the specified bucket. + * + * @param string $bucket (Required) The name of the bucket to use. + * @param string $filename (Required) The file name for the object. + * @return boolean A value of true if the object exists, or a value of false if it does not. + */ + public function if_object_exists($bucket, $filename) + { + if ($this->use_batch_flow) + { + throw new S3_Exception(__FUNCTION__ . '() cannot be batch requested'); + } + + $header = $this->get_object_headers($bucket, $filename); + + if ($header->isOK()) { return true; } + elseif ($header->status === 404) { return false; } + + // @codeCoverageIgnoreStart + return null; + // @codeCoverageIgnoreEnd + } + + /** + * Gets whether or not the specified Amazon S3 bucket has a bucket policy associated with it. + * + * @param string $bucket (Required) The name of the bucket to use. + * @return boolean A value of true if a bucket policy exists, or a value of false if one does not. + */ + public function if_bucket_policy_exists($bucket) + { + if ($this->use_batch_flow) + { + // @codeCoverageIgnoreStart + throw new S3_Exception(__FUNCTION__ . '() cannot be batch requested'); + // @codeCoverageIgnoreEnd + } + + $response = $this->get_bucket_policy($bucket); + + if ($response->isOK()) { return true; } + elseif ($response->status === 404) { return false; } + + // @codeCoverageIgnoreStart + return null; + // @codeCoverageIgnoreEnd + } + + /** + * Gets the number of Amazon S3 objects in the specified bucket. + * + * @param string $bucket (Required) The name of the bucket to use. + * @return integer The number of Amazon S3 objects in the bucket. + */ + public function get_bucket_object_count($bucket) + { + if ($this->use_batch_flow) + { + // @codeCoverageIgnoreStart + throw new S3_Exception(__FUNCTION__ . '() cannot be batch requested'); + // @codeCoverageIgnoreEnd + } + + return count($this->get_object_list($bucket)); + } + + /** + * Gets the cumulative file size of the contents of the Amazon S3 bucket. + * + * @param string $bucket (Required) The name of the bucket to use. + * @param boolean $friendly_format (Optional) A value of true will format the return value to 2 decimal points using the largest possible unit (i.e., 3.42 GB). A value of false will format the return value as the raw number of bytes. + * @return integer|string The number of bytes as an integer, or the friendly format as a string. + */ + public function get_bucket_filesize($bucket, $friendly_format = false) + { + if ($this->use_batch_flow) + { + throw new S3_Exception(__FUNCTION__ . '() cannot be batch requested'); + } + + $filesize = 0; + $list = $this->list_objects($bucket); + + foreach ($list->body->Contents as $filename) + { + $filesize += (integer) $filename->Size; + } + + while ((string) $list->body->IsTruncated === 'true') + { + $body = (array) $list->body; + $list = $this->list_objects($bucket, array( + 'marker' => (string) end($body['Contents'])->Key + )); + + foreach ($list->body->Contents as $object) + { + $filesize += (integer) $object->Size; + } + } + + if ($friendly_format) + { + $filesize = $this->util->size_readable($filesize); + } + + return $filesize; + } + + /** + * Gets the file size of the specified Amazon S3 object. + * + * @param string $bucket (Required) The name of the bucket to use. + * @param string $filename (Required) The file name for the object. + * @param boolean $friendly_format (Optional) A value of true will format the return value to 2 decimal points using the largest possible unit (i.e., 3.42 GB). A value of false will format the return value as the raw number of bytes. + * @return integer|string The number of bytes as an integer, or the friendly format as a string. + */ + public function get_object_filesize($bucket, $filename, $friendly_format = false) + { + if ($this->use_batch_flow) + { + throw new S3_Exception(__FUNCTION__ . '() cannot be batch requested'); + } + + $object = $this->get_object_headers($bucket, $filename); + $filesize = (integer) $object->header['content-length']; + + if ($friendly_format) + { + $filesize = $this->util->size_readable($filesize); + } + + return $filesize; + } + + /** + * Changes the content type for an existing Amazon S3 object. + * + * @param string $bucket (Required) The name of the bucket to use. + * @param string $filename (Required) The file name for the object. + * @param string $contentType (Required) The content-type to apply to the object. + * @param array $opt (Optional) An associative array of parameters that can have the following keys:
    + *
  • curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
  • + *
  • returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
+ * @return CFResponse A object containing a parsed HTTP response. + */ + public function change_content_type($bucket, $filename, $contentType, $opt = null) + { + if (!$opt) $opt = array(); + + // Retrieve the original metadata + $metadata = $this->get_object_metadata($bucket, $filename); + if ($metadata && $metadata['ACL']) + { + $opt['acl'] = $metadata['ACL']; + } + if ($metadata && $metadata['StorageClass']) + { + $opt['headers']['x-amz-storage-class'] = $metadata['StorageClass']; + } + + // Merge optional parameters + $opt = array_merge_recursive(array( + 'headers' => array( + 'Content-Type' => $contentType + ), + 'metadataDirective' => 'COPY' + ), $opt); + + return $this->copy_object( + array('bucket' => $bucket, 'filename' => $filename), + array('bucket' => $bucket, 'filename' => $filename), + $opt + ); + } + + /** + * Changes the storage redundancy for an existing object. + * + * @param string $bucket (Required) The name of the bucket to use. + * @param string $filename (Required) The file name for the object. + * @param string $storage (Required) The storage setting to apply to the object. [Allowed values: AmazonS3::STORAGE_STANDARD, AmazonS3::STORAGE_REDUCED] + * @param array $opt (Optional) An associative array of parameters that can have the following keys:
    + *
  • curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
  • + *
  • returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
+ * @return CFResponse A object containing a parsed HTTP response. + */ + public function change_storage_redundancy($bucket, $filename, $storage, $opt = null) + { + if (!$opt) $opt = array(); + + // Retrieve the original metadata + $metadata = $this->get_object_metadata($bucket, $filename); + if ($metadata && $metadata['ACL']) + { + $opt['acl'] = $metadata['ACL']; + } + if ($metadata && $metadata['ContentType']) + { + $opt['headers']['Content-Type'] = $metadata['ContentType']; + } + + // Merge optional parameters + $opt = array_merge(array( + 'storage' => $storage, + 'metadataDirective' => 'COPY', + ), $opt); + + return $this->copy_object( + array('bucket' => $bucket, 'filename' => $filename), + array('bucket' => $bucket, 'filename' => $filename), + $opt + ); + } + + /** + * Gets a simplified list of bucket names on an Amazon S3 account. + * + * @param string $pcre (Optional) A Perl-Compatible Regular Expression (PCRE) to filter the bucket names against. + * @return array The list of matching bucket names. If there are no results, the method will return an empty array. + * @link http://php.net/pcre Regular Expressions (Perl-Compatible) + */ + public function get_bucket_list($pcre = null) + { + if ($this->use_batch_flow) + { + throw new S3_Exception(__FUNCTION__ . '() cannot be batch requested'); + } + + // Get a list of buckets. + $list = $this->list_buckets(); + if ($list = $list->body->query('descendant-or-self::Name')) + { + $list = $list->map_string($pcre); + return $list; + } + + // @codeCoverageIgnoreStart + return array(); + // @codeCoverageIgnoreEnd + } + + /** + * Gets a simplified list of Amazon S3 object file names contained in a bucket. + * + * @param string $bucket (Required) The name of the bucket to use. + * @param array $opt (Optional) An associative array of parameters that can have the following keys:
    + *
  • delimiter - string - Optional - Keys that contain the same string between the prefix and the first occurrence of the delimiter will be rolled up into a single result element in the CommonPrefixes collection.
  • + *
  • marker - string - Optional - Restricts the response to contain results that only occur alphabetically after the value of the marker.
  • + *
  • max-keys - integer - Optional - The maximum number of results returned by the method call. The returned list will contain no more results than the specified value, but may return less. A value of zero is treated as if you did not specify max-keys.
  • + *
  • pcre - string - Optional - A Perl-Compatible Regular Expression (PCRE) to filter the names against. This is applied only AFTER any native Amazon S3 filtering from specified prefix, marker, max-keys, or delimiter values are applied.
  • + *
  • prefix - string - Optional - Restricts the response to contain results that begin only with the specified prefix.
  • + *
  • curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
  • + * @return array The list of matching object names. If there are no results, the method will return an empty array. + * @link http://php.net/pcre Regular Expressions (Perl-Compatible) + */ + public function get_object_list($bucket, $opt = null) + { + if ($this->use_batch_flow) + { + throw new S3_Exception(__FUNCTION__ . '() cannot be batch requested'); + } + + if (!$opt) $opt = array(); + unset($opt['returnCurlHandle']); // This would cause problems + + // Set some default values + $pcre = isset($opt['pcre']) ? $opt['pcre'] : null; + $max_keys = (isset($opt['max-keys']) && is_int($opt['max-keys'])) ? $opt['max-keys'] : null; + $objects = array(); + + if (!$max_keys) + { + // No max-keys specified. Get everything. + do + { + $list = $this->list_objects($bucket, $opt); + if ($keys = $list->body->query('descendant-or-self::Key')->map_string($pcre)) + { + $objects = array_merge($objects, $keys); + } + + $body = (array) $list->body; + $opt = array_merge($opt, array( + 'marker' => (isset($body['Contents']) && is_array($body['Contents'])) ? + ((string) end($body['Contents'])->Key) : + ((string) $list->body->Contents->Key) + )); + } + while ((string) $list->body->IsTruncated === 'true'); + } + else + { + // Max-keys specified. Approximate number of loops and make the requests. + + $max_keys = $opt['max-keys']; + $loops = ceil($max_keys / 1000); + + do + { + $list = $this->list_objects($bucket, $opt); + $keys = $list->body->query('descendant-or-self::Key')->map_string($pcre); + + if ($count = count($keys)) + { + $objects = array_merge($objects, $keys); + + if ($count < 1000) + { + break; + } + } + + if ($max_keys > 1000) + { + $max_keys -= 1000; + } + + $body = (array) $list->body; + $opt = array_merge($opt, array( + 'max-keys' => $max_keys, + 'marker' => (isset($body['Contents']) && is_array($body['Contents'])) ? + ((string) end($body['Contents'])->Key) : + ((string) $list->body->Contents->Key) + )); + } + while (--$loops); + } + + return $objects; + } + + /** + * Deletes all Amazon S3 objects inside the specified bucket. + * + * @param string $bucket (Required) The name of the bucket to use. + * @param string $pcre (Optional) A Perl-Compatible Regular Expression (PCRE) to filter the names against. The default value is . + * @return boolean A value of true means that all objects were successfully deleted. A value of false means that at least one object failed to delete. + * @link http://php.net/pcre Regular Expressions (Perl-Compatible) + */ + public function delete_all_objects($bucket, $pcre = self::PCRE_ALL) + { + // Collect all matches + $list = $this->get_object_list($bucket, array('pcre' => $pcre)); + + // As long as we have at least one match... + if (count($list) > 0) + { + $objects = array(); + + foreach ($list as $object) + { + $objects[] = array('key' => $object); + } + + $batch = new CFBatchRequest(); + $batch->use_credentials($this->credentials); + + foreach (array_chunk($objects, 1000) as $object_set) + { + $this->batch($batch)->delete_objects($bucket, array( + 'objects' => $object_set + )); + } + + $responses = $this->batch($batch)->send(); + $is_ok = true; + + foreach ($responses as $response) + { + if (!$response->isOK() || isset($response->body->Error)) + { + $is_ok = false; + } + } + + return $is_ok; + } + + // If there are no matches, return true + return true; + } + + /** + * Deletes all of the versions of all Amazon S3 objects inside the specified bucket. + * + * @param string $bucket (Required) The name of the bucket to use. + * @param string $pcre (Optional) A Perl-Compatible Regular Expression (PCRE) to filter the names against. The default value is . + * @return boolean A value of true means that all object versions were successfully deleted. A value of false means that at least one object/version failed to delete. + * @link http://php.net/pcre Regular Expressions (Perl-Compatible) + */ + public function delete_all_object_versions($bucket, $pcre = null) + { + // Instantiate + $versions = $this->list_bucket_object_versions($bucket); + + // Gather all nodes together into a single array + if ($versions->body->DeleteMarker() && $versions->body->Version()) + { + $markers = array_merge($versions->body->DeleteMarker()->getArrayCopy(), $versions->body->Version()->getArrayCopy()); + } + elseif ($versions->body->DeleteMarker()) + { + $markers = $versions->body->DeleteMarker()->getArrayCopy(); + } + elseif ($versions->body->Version()) + { + $markers = $versions->body->Version()->getArrayCopy(); + } + else + { + $markers = array(); + } + + while ((string) $versions->body->IsTruncated === 'true') + { + $versions = $this->list_bucket_object_versions($bucket, array( + 'key-marker' => (string) $versions->body->NextKeyMarker + )); + + // Gather all nodes together into a single array + if ($versions->body->DeleteMarker() && $versions->body->Version()) + { + $markers = array_merge($markers, $versions->body->DeleteMarker()->getArrayCopy(), $versions->body->Version()->getArrayCopy()); + } + elseif ($versions->body->DeleteMarker()) + { + $markers = array_merge($markers, $versions->body->DeleteMarker()->getArrayCopy()); + } + elseif ($versions->body->Version()) + { + $markers = array_merge($markers, $versions->body->Version()->getArrayCopy()); + } + } + + $objects = array(); + + // Loop through markers + foreach ($markers as $marker) + { + if ($pcre) + { + if (preg_match($pcre, (string) $marker->Key)) + { + $xx = array('key' => (string) $marker->Key); + if ((string) $marker->VersionId !== 'null') + { + $xx['version_id'] = (string) $marker->VersionId; + } + $objects[] = $xx; + unset($xx); + } + } + else + { + $xx = array('key' => (string) $marker->Key); + if ((string) $marker->VersionId !== 'null') + { + $xx['version_id'] = (string) $marker->VersionId; + } + $objects[] = $xx; + unset($xx); + } + } + + $batch = new CFBatchRequest(); + $batch->use_credentials($this->credentials); + + foreach (array_chunk($objects, 1000) as $object_set) + { + $this->batch($batch)->delete_objects($bucket, array( + 'objects' => $object_set + )); + } + + $responses = $this->batch($batch)->send(); + $is_ok = true; + + foreach ($responses as $response) + { + if (!$response->isOK() || isset($response->body->Error)) + { + $is_ok = false; + } + } + + return $is_ok; + } + + /** + * Gets the collective metadata for the given Amazon S3 object. + * + * @param string $bucket (Required) The name of the bucket to use. + * @param string $filename (Required) The file name for the Amazon S3 object. + * @param array $opt (Optional) An associative array of parameters that can have the following keys:
      + *
    • versionId - string - Optional - The version of the object to retrieve. Version IDs are returned in the x-amz-version-id header of any previous object-related request.
    • + *
    • curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
    • + *
    • returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
    + * @return mixed If the object exists, the method returns the collective metadata for the Amazon S3 object. If the object does not exist, the method returns boolean false. + */ + public function get_object_metadata($bucket, $filename, $opt = null) + { + $batch = new CFBatchRequest(); + $this->batch($batch)->get_object_acl($bucket, $filename); // Get ACL info + $this->batch($batch)->get_object_headers($bucket, $filename); // Get content-type + $this->batch($batch)->list_objects($bucket, array( // Get other metadata + 'max-keys' => 1, + 'prefix' => $filename + )); + $response = $this->batch($batch)->send(); + + // Fail if any requests were unsuccessful + if (!$response->areOK()) + { + return false; + } + + $data = array( + 'ACL' => array(), + 'ContentType' => null, + 'ETag' => null, + 'Headers' => null, + 'Key' => null, + 'LastModified' => null, + 'Owner' => array(), + 'Size' => null, + 'StorageClass' => null, + ); + + // Add the content type + $data['ContentType'] = (string) $response[1]->header['content-type']; + + // Add the other metadata (including storage type) + $contents = json_decode(json_encode($response[2]->body->query('descendant-or-self::Contents')->first()), true); + $data = array_merge($data, (is_array($contents) ? $contents : array())); + + // Add ACL info + $grants = $response[0]->body->query('descendant-or-self::Grant'); + $max = count($grants); + + // Add raw header info + $data['Headers'] = $response[1]->header; + foreach (array('_info', 'x-amz-id-2', 'x-amz-request-id', 'cneonction', 'server', 'content-length', 'content-type', 'etag') as $header) + { + unset($data['Headers'][$header]); + } + ksort($data['Headers']); + + if (count($grants) > 0) + { + foreach ($grants as $grant) + { + $dgrant = array( + 'id' => (string) $this->util->try_these(array('ID', 'URI'), $grant->Grantee), + 'permission' => (string) $grant->Permission + ); + + $data['ACL'][] = $dgrant; + } + } + + return $data; + } + + + /*%******************************************************************************************%*/ + // URLS + + /** + * Gets the web-accessible URL for the Amazon S3 object or generates a time-limited signed request for + * a private file. + * + * @param string $bucket (Required) The name of the bucket to use. + * @param string $filename (Required) The file name for the Amazon S3 object. + * @param integer|string $preauth (Optional) Specifies that a presigned URL for this request should be returned. May be passed as a number of seconds since UNIX Epoch, or any string compatible with . + * @param array $opt (Optional) An associative array of parameters that can have the following keys:
      + *
    • https - boolean - Optional - Set to true if you would like the URL be in https mode. Otherwise, the default behavior is always to use http regardless of your SSL settings. + *
    • method - string - Optional - The HTTP method to use for the request. Defaults to a value of GET.
    • + *
    • response - array - Optional - Allows adjustments to specific response headers. Pass an associative array where each key is one of the following: cache-control, content-disposition, content-encoding, content-language, content-type, expires. The expires value should use and be formatted with the DATE_RFC2822 constant.
    • + *
    • torrent - boolean - Optional - A value of true will return a URL to a torrent of the Amazon S3 object. A value of false will return a non-torrent URL. Defaults to false.
    • + *
    • versionId - string - Optional - The version of the object. Version IDs are returned in the x-amz-version-id header of any previous object-related request.
    • + *
    • returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
    + * @return string The file URL, with authentication and/or torrent parameters if requested. + * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/S3_QSAuth.html Using Query String Authentication + */ + public function get_object_url($bucket, $filename, $preauth = 0, $opt = null) + { + // Add this to our request + if (!$opt) $opt = array(); + $opt['verb'] = isset($opt['method']) ? $opt['method'] : 'GET'; + $opt['resource'] = $filename; + $opt['preauth'] = $preauth; + + if (isset($opt['torrent']) && $opt['torrent']) + { + $opt['sub_resource'] = 'torrent'; + unset($opt['torrent']); + } + + // GET responses + if (isset($opt['response'])) + { + foreach ($opt['response'] as $key => $value) + { + $opt['response-' . $key] = $value; + unset($opt['response'][$key]); + } + } + + // Determine whether or not to use SSL + $use_ssl = isset($opt['https']) ? (bool) $opt['https'] : false; + unset($opt['https']); + $current_use_ssl_setting = $this->use_ssl; + + // Authenticate to S3 + $this->use_ssl = $use_ssl; + $response = $this->authenticate($bucket, $opt); + $this->use_ssl = $current_use_ssl_setting; + + return $response; + } + + /** + * Gets the web-accessible URL to a torrent of the Amazon S3 object. The Amazon S3 object's access + * control list settings (ACL) MUST be set to for a valid URL to be returned. + * + * @param string $bucket (Required) The name of the bucket to use. + * @param string $filename (Required) The file name for the object. + * @param integer|string $preauth (Optional) Specifies that a presigned URL for this request should be returned. May be passed as a number of seconds since UNIX Epoch, or any string compatible with . + * @return string The torrent URL, with authentication parameters if requested. + * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/index.html?S3TorrentRetrieve.html Using BitTorrent to Retrieve Objects Stored in Amazon S3 + */ + public function get_torrent_url($bucket, $filename, $preauth = 0) + { + return $this->get_object_url($bucket, $filename, $preauth, array( + 'torrent' => true + )); + } + + + /*%******************************************************************************************%*/ + // VERSIONING + + /** + * Enables versioning support for the specified Amazon S3 bucket. + * + * @param string $bucket (Required) The name of the bucket to use. + * @param array $opt (Optional) An associative array of parameters that can have the following keys:
      + *
    • MFASerial - string (Optional) The serial number on the back of the Gemalto device. MFASerial, MFAToken and MFAStatus must all be set for MFA to work.
    • + *
    • MFAToken - string (Optional) The current token displayed on the Gemalto device. MFASerial, MFAToken and MFAStatus must all be set for MFA to work.
    • + *
    • MFAStatus - string (Optional) The MFA Delete status. Can be Enabled or Disabled. MFASerial, MFAToken and MFAStatus must all be set for MFA to work.
    • + *
    • curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
    • + *
    • returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
    + * @return CFResponse A object containing a parsed HTTP response. + * @link http://aws.amazon.com/mfa/ Multi-Factor Authentication + */ + public function enable_versioning($bucket, $opt = null) + { + if (!$opt) $opt = array(); + + // Add this to our request + $opt['verb'] = 'PUT'; + $opt['sub_resource'] = 'versioning'; + $opt['headers'] = array( + 'Content-Type' => 'application/xml' + ); + + $xml = simplexml_load_string($this->base_versioning_xml); + $xml->addChild('Status', 'Enabled'); + + // Enable MFA delete? + // @codeCoverageIgnoreStart + if (isset($opt['MFASerial']) && isset($opt['MFAToken']) && isset($opt['MFAStatus'])) + { + $xml->addChild('MfaDelete', $opt['MFAStatus']); + $opt['headers']['x-amz-mfa'] = ($opt['MFASerial'] . ' ' . $opt['MFAToken']); + } + // @codeCoverageIgnoreEnd + + $opt['body'] = $xml->asXML(); + + // Authenticate to S3 + return $this->authenticate($bucket, $opt); + } + + /** + * Disables versioning support for the specified Amazon S3 bucket. + * + * @param string $bucket (Required) The name of the bucket to use. + * @param array $opt (Optional) An associative array of parameters that can have the following keys:
      + *
    • MFASerial - string - Optional - The serial number on the back of the Gemalto device. MFASerial, MFAToken and MFAStatus must all be set for MFA to work.
    • + *
    • MFAToken - string - Optional - The current token displayed on the Gemalto device. MFASerial, MFAToken and MFAStatus must all be set for MFA to work.
    • + *
    • MFAStatus - string - Optional - The MFA Delete status. Can be Enabled or Disabled. MFASerial, MFAToken and MFAStatus must all be set for MFA to work.
    • + *
    • curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
    • + *
    • returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
    + * @return CFResponse A object containing a parsed HTTP response. + * @link http://aws.amazon.com/mfa/ Multi-Factor Authentication + */ + public function disable_versioning($bucket, $opt = null) + { + if (!$opt) $opt = array(); + + // Add this to our request + $opt['verb'] = 'PUT'; + $opt['sub_resource'] = 'versioning'; + $opt['headers'] = array( + 'Content-Type' => 'application/xml' + ); + + $xml = simplexml_load_string($this->base_versioning_xml); + $xml->addChild('Status', 'Suspended'); + + // Enable MFA delete? + // @codeCoverageIgnoreStart + if (isset($opt['MFASerial']) && isset($opt['MFAToken']) && isset($opt['MFAStatus'])) + { + $xml->addChild('MfaDelete', $opt['MFAStatus']); + $opt['headers']['x-amz-mfa'] = ($opt['MFASerial'] . ' ' . $opt['MFAToken']); + } + // @codeCoverageIgnoreEnd + + $opt['body'] = $xml->asXML(); + + // Authenticate to S3 + return $this->authenticate($bucket, $opt); + } + + /** + * Gets an Amazon S3 bucket's versioning status. + * + * @param string $bucket (Required) The name of the bucket to use. + * @param array $opt (Optional) An associative array of parameters that can have the following keys:
      + *
    • preauth - integer|string - Optional - Specifies that a presigned URL for this request should be returned. May be passed as a number of seconds since UNIX Epoch, or any string compatible with .
    • + *
    • curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
    • + *
    • returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
    + * @return CFResponse A object containing a parsed HTTP response. + */ + public function get_versioning_status($bucket, $opt = null) + { + if (!$opt) $opt = array(); + $opt['verb'] = 'GET'; + $opt['sub_resource'] = 'versioning'; + + // Authenticate to S3 + return $this->authenticate($bucket, $opt); + } + + /** + * Gets a list of all the versions of Amazon S3 objects in the specified bucket. + * + * @param string $bucket (Required) The name of the bucket to use. + * @param array $opt (Optional) An associative array of parameters that can have the following keys:
      + *
    • delimiter - string - Optional - Unicode string parameter. Keys that contain the same string between the prefix and the first occurrence of the delimiter will be rolled up into a single result element in the CommonPrefixes collection.
    • + *
    • key-marker - string - Optional - Restricts the response to contain results that only occur alphabetically after the value of the key-marker.
    • + *
    • max-keys - string - Optional - Limits the number of results returned in response to your query. Will return no more than this number of results, but possibly less.
    • + *
    • prefix - string - Optional - Restricts the response to only contain results that begin with the specified prefix.
    • + *
    • version-id-marker - string - Optional - Restricts the response to contain results that only occur alphabetically after the value of the version-id-marker.
    • + *
    • preauth - integer|string - Optional - Specifies that a presigned URL for this request should be returned. May be passed as a number of seconds since UNIX Epoch, or any string compatible with .
    • + *
    • curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
    • + *
    • returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
    + * @return CFResponse A object containing a parsed HTTP response. + */ + public function list_bucket_object_versions($bucket, $opt = null) + { + if (!$opt) $opt = array(); + $opt['verb'] = 'GET'; + $opt['sub_resource'] = 'versions'; + + foreach (array('delimiter', 'key-marker', 'max-keys', 'prefix', 'version-id-marker') as $param) + { + if (isset($opt[$param])) + { + $opt['query_string'][$param] = $opt[$param]; + unset($opt[$param]); + } + } + + // Authenticate to S3 + return $this->authenticate($bucket, $opt); + } + + + /*%******************************************************************************************%*/ + // BUCKET POLICIES + + /** + * Sets the policy sub-resource for the specified Amazon S3 bucket. The specified policy replaces any + * policy the bucket already has. + * + * To perform this operation, the caller must be authorized to set a policy for the bucket and have + * PutPolicy permissions. If the caller does not have PutPolicy permissions for the bucket, Amazon S3 + * returns a `403 Access Denied` error. If the caller has the correct permissions but has not been + * authorized by the bucket owner, Amazon S3 returns a `405 Method Not Allowed` error. + * + * @param string $bucket (Required) The name of the bucket to use. + * @param CFPolicy $policy (Required) The JSON policy to use. + * @param array $opt (Optional) An associative array of parameters that can have the following keys:
      + *
    • curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
    • + *
    • returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
    + * @return CFResponse A object containing a parsed HTTP response. + * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/AccessPolicyLanguage.html Appendix: The Access Policy Language + */ + public function set_bucket_policy($bucket, CFPolicy $policy, $opt = null) + { + if (!$opt) $opt = array(); + $opt['verb'] = 'PUT'; + $opt['sub_resource'] = 'policy'; + $opt['body'] = $policy->get_json(); + + // Authenticate to S3 + return $this->authenticate($bucket, $opt); + } + + /** + * Gets the policy of the specified Amazon S3 bucket. + * + * To use this operation, the caller must have GetPolicy permissions for the specified bucket and must be + * the bucket owner. If the caller does not have GetPolicy permissions, this method will generate a + * `403 Access Denied` error. If the caller has the correct permissions but is not the bucket owner, this + * method will generate a `405 Method Not Allowed` error. If the bucket does not have a policy defined for + * it, this method will generate a `404 Policy Not Found` error. + * + * @param string $bucket (Required) The name of the bucket to use. + * @param array $opt (Optional) An associative array of parameters that can have the following keys:
      + *
    • curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
    • + *
    • returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
    + * @return CFResponse A object containing a parsed HTTP response. + */ + public function get_bucket_policy($bucket, $opt = null) + { + if (!$opt) $opt = array(); + $opt['verb'] = 'GET'; + $opt['sub_resource'] = 'policy'; + + // Authenticate to S3 + return $this->authenticate($bucket, $opt); + } + + /** + * Deletes the bucket policy for the specified Amazon S3 bucket. To delete the policy, the caller must + * be the bucket owner and have `DeletePolicy` permissions for the specified bucket. + * + * @param string $bucket (Required) The name of the bucket to use. + * @param array $opt (Optional) An associative array of parameters that can have the following keys:
      + *
    • curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
    • + *
    • returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
    + * @return CFResponse A object containing a parsed HTTP response. If you do not have `DeletePolicy` permissions, Amazon S3 returns a `403 Access Denied` error. If you have the correct permissions, but are not the bucket owner, Amazon S3 returns a `405 Method Not Allowed` error. If the bucket doesn't have a policy, Amazon S3 returns a `204 No Content` error. + */ + public function delete_bucket_policy($bucket, $opt = null) + { + if (!$opt) $opt = array(); + $opt['verb'] = 'DELETE'; + $opt['sub_resource'] = 'policy'; + + // Authenticate to S3 + return $this->authenticate($bucket, $opt); + } + + + /*%******************************************************************************************%*/ + // BUCKET NOTIFICATIONS + + /** + * Enables notifications of specified events for an Amazon S3 bucket. Currently, the + * `s3:ReducedRedundancyLostObject` event is the only event supported for notifications. The + * `s3:ReducedRedundancyLostObject` event is triggered when Amazon S3 detects that it has lost all + * copies of an Amazon S3 object and can no longer service requests for that object. + * + * If the bucket owner and Amazon SNS topic owner are the same, the bucket owner has permission to + * publish notifications to the topic by default. Otherwise, the owner of the topic must create a + * policy to enable the bucket owner to publish to the topic. + * + * By default, only the bucket owner can configure notifications on a bucket. However, bucket owners + * can use bucket policies to grant permission to other users to set this configuration with the + * `s3:PutBucketNotification` permission. + * + * After a PUT operation is called to configure notifications on a bucket, Amazon S3 publishes a test + * notification to ensure that the topic exists and that the bucket owner has permission to publish + * to the specified topic. If the notification is successfully published to the SNS topic, the PUT + * operation updates the bucket configuration and returns the 200 OK responses with a + * `x-amz-sns-test-message-id` header containing the message ID of the test notification sent to topic. + * + * @param string $bucket (Required) The name of the bucket to create bucket notifications for. + * @param string $topic_arn (Required) The SNS topic ARN to send notifications to. + * @param string $event (Required) The event type to listen for. + * @param array $opt (Optional) An associative array of parameters that can have the following keys:
      + *
    • curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
    • + *
    • returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
    + * @return CFResponse A object containing a parsed HTTP response. + * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/NotificationHowTo.html Setting Up Notification of Bucket Events + */ + public function create_bucket_notification($bucket, $topic_arn, $event, $opt = null) + { + if (!$opt) $opt = array(); + $opt['verb'] = 'PUT'; + $opt['sub_resource'] = 'notification'; + $opt['headers'] = array( + 'Content-Type' => 'application/xml' + ); + + $xml = simplexml_load_string($this->base_notification_xml); + $topic_config = $xml->addChild('TopicConfiguration'); + $topic_config->addChild('Topic', $topic_arn); + $topic_config->addChild('Event', $event); + + $opt['body'] = $xml->asXML(); + + // Authenticate to S3 + return $this->authenticate($bucket, $opt); + } + + /** + * Gets the notification configuration of a bucket. Currently, the `s3:ReducedRedundancyLostObject` event + * is the only event supported for notifications. The `s3:ReducedRedundancyLostObject` event is triggered + * when Amazon S3 detects that it has lost all replicas of a Reduced Redundancy Storage object and can no + * longer service requests for that object. + * + * If notifications are not enabled on the bucket, the operation returns an empty + * `NotificatonConfiguration` element. + * + * By default, you must be the bucket owner to read the notification configuration of a bucket. However, + * the bucket owner can use a bucket policy to grant permission to other users to read this configuration + * with the `s3:GetBucketNotification` permission. + * + * @param string $bucket (Required) The name of the bucket to use. + * @param array $opt (Optional) An associative array of parameters that can have the following keys:
      + *
    • curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
    • + *
    • returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
    + * @return CFResponse A object containing a parsed HTTP response. + * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/NotificationHowTo.html Setting Up Notification of Bucket Events + */ + public function get_bucket_notifications($bucket, $opt = null) + { + if (!$opt) $opt = array(); + $opt['verb'] = 'GET'; + $opt['sub_resource'] = 'notification'; + + // Authenticate to S3 + return $this->authenticate($bucket, $opt); + } + + /** + * Empties the list of SNS topics to send notifications to. + * + * @param string $bucket (Required) The name of the bucket to use. + * @param array $opt (Optional) An associative array of parameters that can have the following keys:
      + *
    • curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
    • + *
    • returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
    + * @return CFResponse A object containing a parsed HTTP response. + * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/NotificationHowTo.html Setting Up Notification of Bucket Events + */ + public function delete_bucket_notification($bucket, $opt = null) + { + if (!$opt) $opt = array(); + $opt['verb'] = 'PUT'; + $opt['sub_resource'] = 'notification'; + $opt['body'] = $this->base_notification_xml; + + // Authenticate to S3 + return $this->authenticate($bucket, $opt); + } + + + /*%******************************************************************************************%*/ + // MULTIPART UPLOAD + + /** + * Calculates the correct values for sequentially reading a file for multipart upload. This method should + * be used in conjunction with . + * + * @param integer $filesize (Required) The size in bytes of the entire file. + * @param integer $part_size (Required) The size in bytes of the part of the file to send. + * @return array An array containing key-value pairs. The keys are `seekTo` and `length`. + */ + public function get_multipart_counts($filesize, $part_size) + { + $i = 0; + $sizecount = $filesize; + $values = array(); + + while ($sizecount > 0) + { + $sizecount -= $part_size; + $values[] = array( + 'seekTo' => ($part_size * $i), + 'length' => (($sizecount > 0) ? $part_size : ($sizecount + $part_size)), + ); + $i++; + } + + return $values; + } + + /** + * Initiates a multipart upload and returns an `UploadId`. + * + * @param string $bucket (Required) The name of the bucket to use. + * @param string $filename (Required) The file name for the object. + * @param array $opt (Optional) An associative array of parameters that can have the following keys:
      + *
    • acl - string - Optional - The ACL settings for the specified object. [Allowed values: AmazonS3::ACL_PRIVATE, AmazonS3::ACL_PUBLIC, AmazonS3::ACL_OPEN, AmazonS3::ACL_AUTH_READ, AmazonS3::ACL_OWNER_READ, AmazonS3::ACL_OWNER_FULL_CONTROL]. The default value is ACL_PRIVATE.
    • + *
    • contentType - string - Optional - The type of content that is being sent. The default value is application/octet-stream.
    • + *
    • encryption - string - Optional - The algorithm to use for encrypting the object. [Allowed values: AES256]
    • + *
    • headers - array - Optional - Standard HTTP headers to send along in the request. Accepts an associative array of key-value pairs.
    • + *
    • meta - array - Optional - An associative array of key-value pairs. Any header starting with x-amz-meta-: is considered user metadata. It will be stored with the object and returned when you retrieve the object. The total size of the HTTP request, not including the body, must be less than 4 KB.
    • + *
    • storage - string - Optional - Whether to use Standard or Reduced Redundancy storage. [Allowed values: AmazonS3::STORAGE_STANDARD, AmazonS3::STORAGE_REDUCED]. The default value is STORAGE_STANDARD.
    • + *
    • curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
    • + *
    • returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
    + * @return CFResponse A object containing a parsed HTTP response. + * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAccessPolicy.html REST Access Control Policy + */ + public function initiate_multipart_upload($bucket, $filename, $opt = null) + { + if (!$opt) $opt = array(); + + // Add this to our request + $opt['verb'] = 'POST'; + $opt['resource'] = $filename; + $opt['sub_resource'] = 'uploads'; + $opt['body'] = ''; + + // Handle content type. Can also be passed as an HTTP header. + if (isset($opt['contentType'])) + { + $opt['headers']['Content-Type'] = $opt['contentType']; + unset($opt['contentType']); + } + + // Set a default content type. + if (!isset($opt['headers']['Content-Type'])) + { + $opt['headers']['Content-Type'] = 'application/octet-stream'; + } + + // Handle Access Control Lists. Can also be passed as an HTTP header. + if (isset($opt['acl'])) + { + $opt['headers']['x-amz-acl'] = $opt['acl']; + unset($opt['acl']); + } + + // Handle storage settings. Can also be passed as an HTTP header. + if (isset($opt['storage'])) + { + $opt['headers']['x-amz-storage-class'] = $opt['storage']; + unset($opt['storage']); + } + + // Handle encryption settings. Can also be passed as an HTTP header. + if (isset($opt['encryption'])) + { + $opt['headers']['x-amz-server-side-encryption'] = $opt['encryption']; + unset($opt['encryption']); + } + + // Handle meta tags. Can also be passed as an HTTP header. + if (isset($opt['meta'])) + { + foreach ($opt['meta'] as $meta_key => $meta_value) + { + // e.g., `My Meta Header` is converted to `x-amz-meta-my-meta-header`. + $opt['headers']['x-amz-meta-' . strtolower(str_replace(' ', '-', $meta_key))] = $meta_value; + } + unset($opt['meta']); + } + + // Authenticate to S3 + return $this->authenticate($bucket, $opt); + } + + /** + * Uploads a single part of a multipart upload. The part size cannot be smaller than 5 MB + * or larger than 5 TB. A multipart upload can have no more than 10,000 parts. + * + * Amazon S3 charges for storage as well as requests to the service. Smaller part sizes (and more + * requests) allow for faster failures and better upload reliability. Larger part sizes (and fewer + * requests) costs slightly less but has lower upload reliability. + * + * @param string $bucket (Required) The name of the bucket to use. + * @param string $filename (Required) The file name for the object. + * @param string $upload_id (Required) The upload ID identifying the multipart upload whose parts are being listed. The upload ID is retrieved from a call to . + * @param array $opt (Optional) An associative array of parameters that can have the following keys:
      + *
    • fileUpload - string|resource - Required - The URL/path for the file to upload or an open resource.
    • + *
    • partNumber - integer - Required - The part number order of the multipart upload.
    • + *
    • expect - string - Optional - Specifies that the SDK not send the request body until it receives an acknowledgement. If the message is rejected based on the headers, the body of the message is not sent. For more information, see RFC 2616, section 14.20. The value can also be passed to the header option as Expect. [Allowed values: 100-continue]
    • + *
    • headers - array - Optional - Standard HTTP headers to send along in the request. Accepts an associative array of key-value pairs.
    • + *
    • length - integer - Optional - The size of the part in bytes. For more information, see RFC 2616, section 14.13. The value can also be passed to the header option as Content-Length.
    • + *
    • md5 - string - Optional - The base64 encoded 128-bit MD5 digest of the part data. This header can be used as a message integrity check to verify that the part data is the same data that was originally sent. Although it is optional, we recommend using this mechanism as an end-to-end integrity check. For more information, see RFC 1864. The value can also be passed to the header option as Content-MD5.
    • + *
    • seekTo - integer - Optional - The starting position in bytes for the piece of the file/stream to upload.
    • + *
    • curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
    • + *
    • returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
    + * @return CFResponse A object containing a parsed HTTP response. + */ + public function upload_part($bucket, $filename, $upload_id, $opt = null) + { + if (!$opt) $opt = array(); + + // Add this to our request + $opt['verb'] = 'PUT'; + $opt['resource'] = $filename; + $opt['uploadId'] = $upload_id; + + if (!isset($opt['fileUpload']) || !isset($opt['partNumber'])) + { + throw new S3_Exception('The `fileUpload` and `partNumber` options are both required in ' . __FUNCTION__ . '().'); + } + + // Handle expectation. Can also be passed as an HTTP header. + if (isset($opt['expect'])) + { + $opt['headers']['Expect'] = $opt['expect']; + unset($opt['expect']); + } + + // Handle content length. Can also be passed as an HTTP header. + if (isset($opt['length'])) + { + $opt['headers']['Content-Length'] = $opt['length']; + unset($opt['length']); + } + + // Handle content md5. Can also be passed as an HTTP header. + if (isset($opt['md5'])) + { + $opt['headers']['Content-MD5'] = $opt['md5']; + unset($opt['md5']); + } + + $opt['headers']['Expect'] = '100-continue'; + + // Authenticate to S3 + return $this->authenticate($bucket, $opt); + } + + /** + * Lists the completed parts of an in-progress multipart upload. + * + * @param string $bucket (Required) The name of the bucket to use. + * @param string $filename (Required) The file name for the object. + * @param string $upload_id (Required) The upload ID identifying the multipart upload whose parts are being listed. The upload ID is retrieved from a call to . + * @param array $opt (Optional) An associative array of parameters that can have the following keys:
      + *
    • max-parts - integer - Optional - The maximum number of parts to return in the response body.
    • + *
    • part-number-marker - string - Optional - Restricts the response to contain results that only occur numerically after the value of the part-number-marker.
    • + *
    • curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
    • + *
    • returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
    + * @return CFResponse A object containing a parsed HTTP response. + */ + public function list_parts($bucket, $filename, $upload_id, $opt = null) + { + if (!$opt) $opt = array(); + + // Add this to our request + $opt['verb'] = 'GET'; + $opt['resource'] = $filename; + $opt['uploadId'] = $upload_id; + $opt['query_string'] = array(); + + foreach (array('max-parts', 'part-number-marker') as $param) + { + if (isset($opt[$param])) + { + $opt['query_string'][$param] = $opt[$param]; + unset($opt[$param]); + } + } + + // Authenticate to S3 + return $this->authenticate($bucket, $opt); + } + + /** + * Aborts an in-progress multipart upload. This operation cannot be reversed. + * + * @param string $bucket (Required) The name of the bucket to use. + * @param string $filename (Required) The file name for the object. + * @param string $upload_id (Required) The upload ID identifying the multipart upload whose parts are being listed. The upload ID is retrieved from a call to . + * @param array $opt (Optional) An associative array of parameters that can have the following keys:
      + *
    • curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
    • + *
    • returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
    + * @return CFResponse A object containing a parsed HTTP response. + */ + public function abort_multipart_upload($bucket, $filename, $upload_id, $opt = null) + { + if (!$opt) $opt = array(); + + // Add this to our request + $opt['verb'] = 'DELETE'; + $opt['resource'] = $filename; + $opt['uploadId'] = $upload_id; + + // Authenticate to S3 + return $this->authenticate($bucket, $opt); + } + + /** + * Completes an in-progress multipart upload. A multipart upload is completed by describing the part + * numbers and corresponding ETag values in order, and submitting that data to Amazon S3 as an XML document. + * + * @param string $bucket (Required) The name of the bucket to use. + * @param string $filename (Required) The file name for the object. + * @param string $upload_id (Required) The upload ID identifying the multipart upload whose parts are being listed. The upload ID is retrieved from a call to . + * @param string|array|SimpleXMLElement|CFResponse $parts (Required) The completion XML document. This document can be provided in multiple ways; as a string of XML, as a object representing the XML document, as an indexed array of associative arrays where the keys are PartNumber and ETag, or as a object returned by . + * @param array $opt (Optional) An associative array of parameters that can have the following keys:
      + *
    • curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
    • + *
    • returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
    + * @return CFResponse A object containing a parsed HTTP response. + */ + public function complete_multipart_upload($bucket, $filename, $upload_id, $parts, $opt = null) + { + if (!$opt) $opt = array(); + + // Add this to our request + $opt['verb'] = 'POST'; + $opt['resource'] = $filename; + $opt['uploadId'] = $upload_id; + $opt['headers'] = array( + 'Content-Type' => 'application/xml' + ); + + // Disable Content-MD5 calculation for this operation + $opt['NoContentMD5'] = true; + + if (is_string($parts)) + { + // Assume it's the intended XML. + $opt['body'] = $parts; + } + elseif ($parts instanceof SimpleXMLElement) + { + // Assume it's a SimpleXMLElement object representing the XML. + $opt['body'] = $parts->asXML(); + } + elseif (is_array($parts) || $parts instanceof CFResponse) + { + $xml = simplexml_load_string($this->complete_mpu_xml); + + if (is_array($parts)) + { + // Generate the appropriate XML. + foreach ($parts as $node) + { + $part = $xml->addChild('Part'); + $part->addChild('PartNumber', $node['PartNumber']); + $part->addChild('ETag', $node['ETag']); + } + + } + elseif ($parts instanceof CFResponse) + { + // Assume it's a response from list_parts(). + foreach ($parts->body->Part as $node) + { + $part = $xml->addChild('Part'); + $part->addChild('PartNumber', (string) $node->PartNumber); + $part->addChild('ETag', (string) $node->ETag); + } + } + + $opt['body'] = $xml->asXML(); + } + + // Authenticate to S3 + return $this->authenticate($bucket, $opt); + } + + /** + * Lists the in-progress multipart uploads. + * + * @param string $bucket (Required) The name of the bucket to use. + * @param array $opt (Optional) An associative array of parameters that can have the following keys:
      + *
    • delimiter - string - Optional - Keys that contain the same string between the prefix and the first occurrence of the delimiter will be rolled up into a single result element in the CommonPrefixes collection.
    • + *
    • key-marker - string - Optional - Restricts the response to contain results that only occur alphabetically after the value of the key-marker. If used in conjunction with upload-id-marker, the results will be filtered to include keys whose upload ID is alphabetically after the value of upload-id-marker.
    • + *
    • upload-id-marker - string - Optional - Restricts the response to contain results that only occur alphabetically after the value of the upload-id-marker. Must be used in conjunction with key-marker.
    • + *
    • curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
    • + *
    • returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
    + * @return CFResponse A object containing a parsed HTTP response. + */ + public function list_multipart_uploads($bucket, $opt = null) + { + if (!$opt) $opt = array(); + + // Add this to our request + $opt['verb'] = 'GET'; + $opt['sub_resource'] = 'uploads'; + + foreach (array('key-marker', 'max-uploads', 'upload-id-marker') as $param) + { + if (isset($opt[$param])) + { + $opt['query_string'][$param] = $opt[$param]; + unset($opt[$param]); + } + } + + // Authenticate to S3 + return $this->authenticate($bucket, $opt); + } + + /** + * Since Amazon S3's standard operation only supports copying objects that are smaller than + * 5 GB, the ability to copy large objects (greater than 5 GB) requires the use of "Multipart Copy". + * + * Copying large objects requires the developer to initiate a new multipart "upload", copy pieces of the + * large object (specifying a range of bytes up to 5 GB from the large source file), then complete the + * multipart "upload". + * + * NOTE: This is a synchronous operation, not an asynchronous operation, which means + * that Amazon S3 will not return a response for this operation until the copy has completed across the Amazon + * S3 server fleet. Copying objects within a single region will complete more quickly than copying objects + * across regions. The synchronous nature of this operation is different from other services where + * responses are typically returned immediately, even if the operation itself has not yet been completed on + * the server-side. + * + * @param array $source (Required) The bucket and file name to copy from. The following keys must be set:
      + *
    • bucket - string - Required - Specifies the name of the bucket containing the source object.
    • + *
    • filename - string - Required - Specifies the file name of the source object to copy.
    + * @param array $dest (Required) The bucket and file name to copy to. The following keys must be set:
      + *
    • bucket - string - Required - Specifies the name of the bucket to copy the object to.
    • + *
    • filename - string - Required - Specifies the file name to copy the object to.
    + * @param string $upload_id (Required) The upload ID identifying the multipart upload whose parts are being listed. The upload ID is retrieved from a call to . + * @param integer $part_number (Required) A part number uniquely identifies a part and defines its position within the destination object. When you complete a multipart upload, a complete object is created by concatenating parts in ascending order based on part number. If you copy a new part using the same part number as a previously copied/uploaded part, the previously written part is overwritten. + * @param array $opt (Optional) An associative array of parameters that can have the following keys:
      + *
    • ifMatch - string - Optional - The ETag header from a previous request. Copies the object if its entity tag (ETag) matches the specified tag; otherwise, the request returns a 412 HTTP status code error (precondition failed). Used in conjunction with ifUnmodifiedSince.
    • + *
    • ifUnmodifiedSince - string - Optional - The LastModified header from a previous request. Copies the object if it hasn't been modified since the specified time; otherwise, the request returns a 412 HTTP status code error (precondition failed). Used in conjunction with ifMatch.
    • + *
    • ifNoneMatch - string - Optional - The ETag header from a previous request. Copies the object if its entity tag (ETag) is different than the specified ETag; otherwise, the request returns a 412 HTTP status code error (failed condition). Used in conjunction with ifModifiedSince.
    • + *
    • ifModifiedSince - string - Optional - The LastModified header from a previous request. Copies the object if it has been modified since the specified time; otherwise, the request returns a 412 HTTP status code error (failed condition). Used in conjunction with ifNoneMatch.
    • + *
    • range - string - Optional - The range of bytes to copy from the object. Specify this parameter when copying partial bits. The specified range must be notated with a hyphen (e.g., 0-10485759). Defaults to the byte range of the complete Amazon S3 object.
    • + *
    • versionId - string - Optional - The version of the object to copy. Version IDs are returned in the x-amz-version-id header of any previous object-related request.
    • + *
    • curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
    • + *
    • returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
    + * @return CFResponse A object containing a parsed HTTP response. + */ + public function copy_part($source, $dest, $upload_id, $part_number, $opt = null) + { + if (!$opt) $opt = array(); + + // Add this to our request + $opt['verb'] = 'PUT'; + $opt['resource'] = $dest['filename']; + $opt['uploadId'] = $upload_id; + $opt['partNumber'] = $part_number; + + // Handle copy source + if (isset($source['bucket']) && isset($source['filename'])) + { + $opt['headers']['x-amz-copy-source'] = '/' . $source['bucket'] . '/' . rawurlencode($source['filename']) + . (isset($opt['versionId']) ? ('?' . 'versionId=' . rawurlencode($opt['versionId'])) : ''); // Append the versionId to copy, if available + unset($opt['versionId']); + } + + // Handle conditional-copy parameters + if (isset($opt['ifMatch'])) + { + $opt['headers']['x-amz-copy-source-if-match'] = $opt['ifMatch']; + unset($opt['ifMatch']); + } + if (isset($opt['ifNoneMatch'])) + { + $opt['headers']['x-amz-copy-source-if-none-match'] = $opt['ifNoneMatch']; + unset($opt['ifNoneMatch']); + } + if (isset($opt['ifUnmodifiedSince'])) + { + $opt['headers']['x-amz-copy-source-if-unmodified-since'] = $opt['ifUnmodifiedSince']; + unset($opt['ifUnmodifiedSince']); + } + if (isset($opt['ifModifiedSince'])) + { + $opt['headers']['x-amz-copy-source-if-modified-since'] = $opt['ifModifiedSince']; + unset($opt['ifModifiedSince']); + } + + // Partial content range + if (isset($opt['range'])) + { + $opt['headers']['x-amz-copy-source-range'] = 'bytes=' . $opt['range']; + } + + // Authenticate to S3 + return $this->authenticate($dest['bucket'], $opt); + } + + /** + * Creates an Amazon S3 object using the multipart upload APIs. It is analogous to . + * + * While each individual part of a multipart upload can hold up to 5 GB of data, this method limits the + * part size to a maximum of 500 MB. The combined size of all parts can not exceed 5 TB of data. When an + * object is stored in Amazon S3, the data is streamed to multiple storage servers in multiple data + * centers. This ensures the data remains available in the event of internal network or hardware failure. + * + * Amazon S3 charges for storage as well as requests to the service. Smaller part sizes (and more + * requests) allow for faster failures and better upload reliability. Larger part sizes (and fewer + * requests) costs slightly less but has lower upload reliability. + * + * In certain cases with large objects, it's possible for this method to attempt to open more file system + * connections than allowed by the OS. In this case, either + * increase the number of connections + * allowed or increase the value of the partSize parameter to use a larger part size. + * + * @param string $bucket (Required) The name of the bucket to use. + * @param string $filename (Required) The file name for the object. + * @param array $opt (Optional) An associative array of parameters that can have the following keys:
      + *
    • fileUpload - string|resource - Required - The URL/path for the file to upload, or an open resource.
    • + *
    • acl - string - Optional - The ACL settings for the specified object. [Allowed values: AmazonS3::ACL_PRIVATE, AmazonS3::ACL_PUBLIC, AmazonS3::ACL_OPEN, AmazonS3::ACL_AUTH_READ, AmazonS3::ACL_OWNER_READ, AmazonS3::ACL_OWNER_FULL_CONTROL]. The default value is ACL_PRIVATE.
    • + *
    • contentType - string - Optional - The type of content that is being sent in the body. The default value is application/octet-stream.
    • + *
    • headers - array - Optional - Standard HTTP headers to send along in the request. Accepts an associative array of key-value pairs.
    • + *
    • length - integer - Optional - The size of the object in bytes. For more information, see RFC 2616, section 14.13. The value can also be passed to the header option as Content-Length.
    • + *
    • limit - integer - Optional - The maximum number of concurrent uploads done by cURL. Gets passed to CFBatchRequest.
    • + *
    • meta - array - Optional - An associative array of key-value pairs. Any header starting with x-amz-meta-: is considered user metadata. It will be stored with the object and returned when you retrieve the object. The total size of the HTTP request, not including the body, must be less than 4 KB.
    • + *
    • partSize - integer - Optional - The size of an individual part. The size may not be smaller than 5 MB or larger than 500 MB. The default value is 50 MB.
    • + *
    • seekTo - integer - Optional - The starting position in bytes for the first piece of the file/stream to upload.
    • + *
    • storage - string - Optional - Whether to use Standard or Reduced Redundancy storage. [Allowed values: AmazonS3::STORAGE_STANDARD, AmazonS3::STORAGE_REDUCED]. The default value is STORAGE_STANDARD.
    • + *
    • uploadId - string - Optional - An upload ID identifying an existing multipart upload to use. If this option is not set, one will be created automatically.
    • + *
    • curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
    • + *
    • returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
    + * @return CFResponse A object containing a parsed HTTP response. + * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAccessPolicy.html REST Access Control Policy + */ + public function create_mpu_object($bucket, $filename, $opt = null) + { + if ($this->use_batch_flow) + { + throw new S3_Exception(__FUNCTION__ . '() cannot be batch requested'); + } + + if (!$opt) $opt = array(); + + // Handle content length. Can also be passed as an HTTP header. + if (isset($opt['length'])) + { + $opt['headers']['Content-Length'] = $opt['length']; + unset($opt['length']); + } + + if (!isset($opt['fileUpload'])) + { + throw new S3_Exception('The `fileUpload` option is required in ' . __FUNCTION__ . '().'); + } + elseif (is_resource($opt['fileUpload'])) + { + $opt['limit'] = 1; // We can only read from this one resource. + $upload_position = isset($opt['seekTo']) ? (integer) $opt['seekTo'] : ftell($opt['fileUpload']); + $upload_filesize = isset($opt['headers']['Content-Length']) ? (integer) $opt['headers']['Content-Length'] : null; + + if (!isset($upload_filesize) && $upload_position !== false) + { + $stats = fstat($opt['fileUpload']); + + if ($stats && $stats['size'] >= 0) + { + $upload_filesize = $stats['size'] - $upload_position; + } + } + } + else + { + $upload_position = isset($opt['seekTo']) ? (integer) $opt['seekTo'] : 0; + + if (isset($opt['headers']['Content-Length'])) + { + $upload_filesize = (integer) $opt['headers']['Content-Length']; + } + else + { + $upload_filesize = filesize($opt['fileUpload']); + + if ($upload_filesize !== false) + { + $upload_filesize -= $upload_position; + } + } + } + + if ($upload_position === false || !isset($upload_filesize) || $upload_filesize === false || $upload_filesize < 0) + { + throw new S3_Exception('The size of `fileUpload` cannot be determined in ' . __FUNCTION__ . '().'); + } + + // Handle part size + if (isset($opt['partSize'])) + { + // If less that 5 MB... + if ((integer) $opt['partSize'] < 5242880) + { + $opt['partSize'] = 5242880; // 5 MB + } + // If more than 500 MB... + elseif ((integer) $opt['partSize'] > 524288000) + { + $opt['partSize'] = 524288000; // 500 MB + } + } + else + { + $opt['partSize'] = 52428800; // 50 MB + } + + // If the upload size is smaller than the piece size, failover to create_object(). + if ($upload_filesize < $opt['partSize'] && !isset($opt['uploadId'])) + { + return $this->create_object($bucket, $filename, $opt); + } + + // Initiate multipart upload + if (isset($opt['uploadId'])) + { + $upload_id = $opt['uploadId']; + } + else + { + // Compose options for initiate_multipart_upload(). + $_opt = array(); + foreach (array('contentType', 'acl', 'storage', 'headers', 'meta') as $param) + { + if (isset($opt[$param])) + { + $_opt[$param] = $opt[$param]; + } + } + + $upload = $this->initiate_multipart_upload($bucket, $filename, $_opt); + if (!$upload->isOK()) + { + return $upload; + } + + // Fetch the UploadId + $upload_id = (string) $upload->body->UploadId; + } + + // Get the list of pieces + $pieces = $this->get_multipart_counts($upload_filesize, (integer) $opt['partSize']); + + // Queue batch requests + $batch = new CFBatchRequest(isset($opt['limit']) ? (integer) $opt['limit'] : null); + foreach ($pieces as $i => $piece) + { + $this->batch($batch)->upload_part($bucket, $filename, $upload_id, array( + 'expect' => '100-continue', + 'fileUpload' => $opt['fileUpload'], + 'partNumber' => ($i + 1), + 'seekTo' => $upload_position + (integer) $piece['seekTo'], + 'length' => (integer) $piece['length'], + )); + } + + // Send batch requests + $batch_responses = $this->batch($batch)->send(); + if (!$batch_responses->areOK()) + { + return $batch_responses; + } + + // Compose completion XML + $parts = array(); + foreach ($batch_responses as $i => $response) + { + $parts[] = array('PartNumber' => ($i + 1), 'ETag' => $response->header['etag']); + } + + return $this->complete_multipart_upload($bucket, $filename, $upload_id, $parts); + } + + /** + * Aborts all multipart uploads initiated before the specified date. This operation cannot be reversed. + * + * @param string $bucket (Required) The name of the bucket to use. + * @param string|integer $when (Optional) The time and date to use for comparison. Accepts any value that understands. + * @return CFArray A containing a series of 0 or more objects, containing a parsed HTTP response. + */ + public function abort_multipart_uploads_by_date($bucket, $when = null) + { + if ($this->use_batch_flow) + { + // @codeCoverageIgnoreStart + throw new S3_Exception(__FUNCTION__ . '() cannot be batch requested'); + // @codeCoverageIgnoreEnd + } + + $when = $when ? $when : time(); + $handles = array(); + $data = $this->list_multipart_uploads($bucket)->body; + $when = is_int($when) ? $when : strtotime($when); + + if (!($data instanceof CFSimpleXML)) + { + return false; + } + + $list = $data->query('descendant-or-self::Upload/Initiated'); + + if (count($list) > 0) + { + foreach ($list as $node) + { + if (strtotime((string) $node) < $when) + { + $q = new CFBatchRequest(); + $parent = $node->parent(); + + $upload_id = $parent + ->query('descendant-or-self::UploadId') + ->first() + ->to_string(); + + $filename = $parent + ->query('descendant-or-self::Key') + ->first() + ->to_string(); + + $handles[] = $this->abort_multipart_upload($bucket, $filename, $upload_id, array( + 'returnCurlHandle' => true + )); + } + } + + $http = new CFRequest(); + $responses = $http->send_multi_request($handles); + + if (is_array($responses) && count($responses) > 0) + { + return new CFArray($responses); + } + } + + return new CFArray(); + } + + + /*%******************************************************************************************%*/ + // WEBSITE CONFIGURATION + + /** + * Enables and configures an Amazon S3 website using the corresponding bucket as the content source. + * The website will have one default domain name associated with it, which is the bucket name. If you + * attempt to configure an Amazon S3 website for a bucket whose name is not compatible with DNS, + * Amazon S3 returns an InvalidBucketName error. For more information on bucket names and DNS, + * refer to Bucket Restrictions and Limitations. + * + * To visit the bucket as a website a new endpoint is created in the following pattern: + * http://<bucketName>.s3-website-<region>.amazonaws.com. This is a sample URL + * for a bucket called example-bucket in the us-east-1 region. + * (e.g., http://example-bucket.s3-website-us-east-1.amazonaws.com) + * + * @param string $bucket (Required) The name of the bucket to use. + * @param array $opt (Optional) An associative array of parameters that can have the following keys:
      + *
    • indexDocument - string - Optional - The file path to use as the root document. The default value is index.html.
    • + *
    • errorDocument - string - Optional - The file path to use as the error document. The default value is error.html.
    • + *
    • curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
    • + *
    • returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
    + * @return CFResponse A object containing a parsed HTTP response. + */ + public function create_website_config($bucket, $opt = null) + { + if (!$opt) $opt = array(); + $opt['verb'] = 'PUT'; + $opt['sub_resource'] = 'website'; + + $xml = simplexml_load_string($this->website_config_xml); + if (isset($opt['indexDocument'])) + { + $xml->IndexDocument->Suffix = $opt['indexDocument']; + } + if (isset($opt['errorDocument'])) + { + $xml->ErrorDocument->Key = $opt['errorDocument']; + } + + $opt['body'] = $xml->asXML(); + + // Authenticate to S3 + return $this->authenticate($bucket, $opt); + } + + /** + * Retrieves the website configuration for a bucket. The contents of this response are identical to the + * content submitted by the user during the website creation operation. If a website configuration has + * never been set, Amazon S3 will return a 404 error. + * + * @param string $bucket (Required) The name of the bucket to use. + * @param array $opt (Optional) An associative array of parameters that can have the following keys:
      + *
    • curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
    • + *
    • returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
    + * @return CFResponse A object containing a parsed HTTP response. + */ + public function get_website_config($bucket, $opt = null) + { + if (!$opt) $opt = array(); + $opt['verb'] = 'GET'; + $opt['sub_resource'] = 'website'; + $opt['headers'] = array( + 'Content-Type' => 'application/xml' + ); + + // Authenticate to S3 + return $this->authenticate($bucket, $opt); + } + + /** + * Removes the website configuration for a bucket. + * + * @param string $bucket (Required) The name of the bucket to use. + * @param array $opt (Optional) An associative array of parameters that can have the following keys:
      + *
    • curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
    • + *
    • returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
    + * @return CFResponse A object containing a parsed HTTP response. + */ + public function delete_website_config($bucket, $opt = null) + { + if (!$opt) $opt = array(); + $opt['verb'] = 'DELETE'; + $opt['sub_resource'] = 'website'; + + // Authenticate to S3 + return $this->authenticate($bucket, $opt); + } + + + /*%******************************************************************************************%*/ + // OBJECT EXPIRATION + + /** + * Enables the ability to specify an expiry period for objects when an object should be deleted, + * measured as number of days from creation time. Amazon S3 guarantees that the object will be + * deleted when the expiration time is passed. + * + * @param string $bucket (Required) The name of the bucket to use. + * @param array $opt (Optional) An associative array of parameters that can have the following keys:
      + *
    • rules - string - Required - The object expiration rule-sets to apply to the bucket.
        + *
      • x - array - Required - This represents a simple array index.
          + *
        • id - string - Optional - Unique identifier for the rule. The value cannot be longer than 255 characters. + *
        • prefix - string - Required - The Amazon S3 object prefix which targets the file(s) for expiration.
        • + *
        • expiration - array - Required - The container for the unit of measurement by which the expiration time is calculated.
            + *
          • days - integer - Required - The number of days until the targetted objects expire from the bucket.
          • + *
        • + *
        • enabled - boolean - Optional - Whether or not to enable this rule-set. A value of true enables the rule-set. A value of false disables the rule-set. The default value is true.
        • + *
      • + *
    • + *
    • curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
    • + *
    • returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
    + * @return CFResponse A object containing a parsed HTTP response. + */ + public function create_object_expiration_config($bucket, $opt = null) + { + if (!$opt) $opt = array(); + $opt['verb'] = 'PUT'; + $opt['sub_resource'] = 'lifecycle'; + $opt['headers'] = array( + 'Content-Type' => 'application/xml' + ); + + $xml = simplexml_load_string($this->object_expiration_xml, $this->parser_class); + + if (isset($opt['rules']) && is_array($opt['rules']) && count($opt['rules'])) + { + foreach ($opt['rules'] as $rule) + { + $xrule = $xml->addChild('Rule'); + + // ID + if (isset($rule['id'])) + { + if (strlen($rule['id']) > 255) + { + throw new S3_Exception('The "id" for a rule must not be more than 255 characters in the ' . __FUNCTION__ . ' method.'); + } + + $xrule->addChild('ID', $rule['id']); + } + + // Prefix + if (isset($rule['prefix'])) + { + $xrule->addChild('Prefix', $rule['prefix']); + } + else + { + throw new S3_Exception('Each rule requires a "prefix" in the ' . __FUNCTION__ . ' method.'); + } + + // Status + $enabled = 'Enabled'; + if (isset($rule['enabled'])) + { + if (is_bool($rule['enabled'])) // Boolean + { + $enabled = $rule['enabled'] ? 'Enabled' : 'Disabled'; + } + elseif (is_string($rule['enabled'])) // String + { + $enabled = (strtolower($rule['enabled']) === 'true') ? 'Enabled' : 'Disabled'; + } + + $xrule->addChild('Status', $enabled); + } + else + { + $xrule->addChild('Status', 'Enabled'); + } + + // Expiration + if (isset($rule['expiration'])) + { + $xexpiration = $xrule->addChild('Expiration'); + + if (isset($rule['expiration']['days'])) + { + $xexpiration->addChild('Days', $rule['expiration']['days']); + } + } + else + { + throw new S3_Exception('Each rule requires a "expiration" in the ' . __FUNCTION__ . ' method.'); + } + } + } + + $opt['body'] = $xml->asXML(); + + // Authenticate to S3 + return $this->authenticate($bucket, $opt); + } + + public function get_object_expiration_config($bucket, $opt = null) + { + if (!$opt) $opt = array(); + $opt['verb'] = 'GET'; + $opt['sub_resource'] = 'lifecycle'; + + // Authenticate to S3 + return $this->authenticate($bucket, $opt); + } + + public function delete_object_expiration_config($bucket, $opt = null) + { + if (!$opt) $opt = array(); + $opt['verb'] = 'DELETE'; + $opt['sub_resource'] = 'lifecycle'; + + // Authenticate to S3 + return $this->authenticate($bucket, $opt); + } + + + /*%******************************************************************************************%*/ + // MISCELLANEOUS + + /** + * Gets the canonical user ID and display name from the Amazon S3 server. + * + * @return array An associative array containing the `id` and `display_name` values. + */ + public function get_canonical_user_id() + { + if ($this->use_batch_flow) + { + throw new S3_Exception(__FUNCTION__ . '() cannot be batch requested'); + } + + $id = $this->list_buckets(); + + return array( + 'id' => (string) $id->body->Owner->ID, + 'display_name' => (string) $id->body->Owner->DisplayName + ); + } + + /** + * Loads and registers the S3StreamWrapper class as a stream wrapper. + * + * @param string $protocol (Optional) The name of the protocol to register. + * @return boolean Whether or not the registration succeeded. + */ + public function register_stream_wrapper($protocol = 's3') + { + require_once dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR . 'extensions' + . DIRECTORY_SEPARATOR . 's3streamwrapper.class.php'; + + return S3StreamWrapper::register($this, $protocol); + } +} diff --git a/3rdparty/aws-sdk/utilities/array.class.php b/3rdparty/aws-sdk/utilities/array.class.php new file mode 100644 index 0000000000..dea673546f --- /dev/null +++ b/3rdparty/aws-sdk/utilities/array.class.php @@ -0,0 +1,312 @@ + object extends PHP's built-in object by providing convenience methods for + * rapidly manipulating array data. Specifically, the `CFArray` object is intended for working with + * and objects that are returned by AWS services. + * + * @version 2012.01.17 + * @license See the included NOTICE.md file for more information. + * @copyright See the included NOTICE.md file for more information. + * @link http://aws.amazon.com/php/ PHP Developer Center + * @link http://php.net/ArrayObject ArrayObject + */ +class CFArray extends ArrayObject +{ + /** + * Constructs a new instance of . + * + * @param mixed $input (Optional) The input parameter accepts an array or an Object. The default value is an empty array. + * @param integer $flags (Optional) Flags to control the behavior of the ArrayObject object. Defaults to . + * @param string $iterator_class (Optional) Specify the class that will be used for iteration of the object. is the default class used. + * @return mixed Either an array of matches, or a single element. + */ + public function __construct($input = array(), $flags = self::STD_PROP_LIST, $iterator_class = 'ArrayIterator') + { + // Provide a default value + $input = $input ? $input : array(); + + try { + return parent::__construct($input, $flags, $iterator_class); + } + catch (InvalidArgumentException $e) + { + throw new CFArray_Exception($e->getMessage()); + } + } + + /** + * Alternate approach to constructing a new instance. Supports chaining. + * + * @param mixed $input (Optional) The input parameter accepts an array or an Object. The default value is an empty array. + * @param integer $flags (Optional) Flags to control the behavior of the ArrayObject object. Defaults to . + * @param string $iterator_class (Optional) Specify the class that will be used for iteration of the object. is the default class used. + * @return mixed Either an array of matches, or a single element. + */ + public static function init($input = array(), $flags = self::STD_PROP_LIST, $iterator_class = 'ArrayIterator') + { + if (version_compare(PHP_VERSION, '5.3.0', '<')) + { + throw new Exception('PHP 5.3 or newer is required to instantiate a new class with CLASS::init().'); + } + + $self = get_called_class(); + return new $self($input, $flags, $iterator_class); + } + + /** + * Handles how the object is rendered when cast as a string. + * + * @return string The word "Array". + */ + public function __toString() + { + return 'Array'; + } + + + /*%******************************************************************************************%*/ + // REFORMATTING + + /** + * Maps each element in the object as an integer. + * + * @return array The contents of the object mapped as integers. + */ + public function map_integer() + { + return array_map('intval', $this->getArrayCopy()); + } + + /** + * Maps each element in the CFArray object as a string. + * + * @param string $pcre (Optional) A Perl-Compatible Regular Expression (PCRE) to filter the names against. + * @return array The contents of the object mapped as strings. If there are no results, the method will return an empty array. + */ + public function map_string($pcre = null) + { + $list = array_map('strval', $this->getArrayCopy()); + $dlist = array(); + + if ($pcre) + { + foreach ($list as $item) + { + $dlist[] = preg_match($pcre, $item) ? $item : null; + } + + $list = array_values(array_filter($dlist)); + } + + return $list; + } + + + /*%******************************************************************************************%*/ + // CONFIRMATION + + /** + * Verifies that _all_ responses were successful. A single failed request will cause to return false. Equivalent to , except it applies to all responses. + * + * @return boolean Whether _all_ requests were successful or not. + */ + public function areOK() + { + $dlist = array(); + $list = $this->getArrayCopy(); + + foreach ($list as $response) + { + if ($response instanceof CFResponse) + { + $dlist[] = $response->isOK(); + } + } + + return (array_search(false, $dlist, true) !== false) ? false : true; + } + + + /*%******************************************************************************************%*/ + // ITERATING AND EXECUTING + + /** + * Iterates over a object, and executes a function for each matched element. + * + * The callback function takes three parameters:
      + *
    • $item - mixed - Optional - The individual node in the array.
    • + *
    • $key - mixed - Optional - The key for the array node.
    • + *
    • $bind - mixed - Optional - The variable that was passed into the $bind parameter.
    + * + * @param string|function $callback (Required) The callback function to execute. PHP 5.3 or newer can use an anonymous function. + * @param mixed $bind (Optional) A variable from the calling scope to pass-by-reference into the local scope of the callback function. + * @return CFArray The original object. + */ + public function each($callback, &$bind = null) + { + $items = $this->getArrayCopy(); + + foreach ($items as $key => &$item) + { + $callback($item, $key, $bind); + } + + return $this; + } + + /** + * Passes each element in the current object through a function, and produces a new object containing the return values. + * + * The callback function takes three parameters:
      + *
    • $item - mixed - Optional - The individual node in the array.
    • + *
    • $key - mixed - Optional - The key for the array node.
    • + *
    • $bind - mixed - Optional - The variable that was passed into the $bind parameter.
    + * + * @param string|function $callback (Required) The callback function to execute. PHP 5.3 or newer can use an anonymous function. + * @param mixed $bind (Optional) A variable from the calling scope to pass-by-reference into the local scope of the callback function. + * @return CFArray A new object containing the return values. + */ + public function map($callback, &$bind = null) + { + $items = $this->getArrayCopy(); + $collect = array(); + + foreach ($items as $key => &$item) + { + $collect[] = $callback($item, $key, $bind); + } + + return new CFArray($collect); + } + + /** + * Filters the list of nodes by passing each value in the current object through a function. The node will be removed if the function returns `false`. + * + * The callback function takes three parameters:
      + *
    • $item - mixed - Optional - The individual node in the array.
    • + *
    • $key - mixed - Optional - The key for the array node.
    • + *
    • $bind - mixed - Optional - The variable that was passed into the $bind parameter.
    + * + * @param string|function $callback (Required) The callback function to execute. PHP 5.3 or newer can use an anonymous function. + * @param mixed $bind (Optional) A variable from the calling scope to pass-by-reference into the local scope of the callback function. + * @return CFArray A new object containing the return values. + */ + public function filter($callback, &$bind = null) + { + $items = $this->getArrayCopy(); + $collect = array(); + + foreach ($items as $key => &$item) + { + if ($callback($item, $key, $bind) !== false) + { + $collect[] = $item; + } + } + + return new CFArray($collect); + } + + /** + * Alias for . This functionality was incorrectly named _reduce_ in earlier versions of the SDK. + * + * @param string|function $callback (Required) The callback function to execute. PHP 5.3 or newer can use an anonymous function. + * @param mixed $bind (Optional) A variable from the calling scope to pass-by-reference into the local scope of the callback function. + * @return CFArray A new object containing the return values. + */ + public function reduce($callback, &$bind = null) + { + return $this->filter($callback, $bind); + } + + + /*%******************************************************************************************%*/ + // TRAVERSAL + + /** + * Gets the first result in the array. + * + * @return mixed The first result in the object. Returns `false` if there are no items in the array. + */ + public function first() + { + $items = $this->getArrayCopy(); + return count($items) ? $items[0] : false; + } + + /** + * Gets the last result in the array. + * + * @return mixed The last result in the object. Returns `false` if there are no items in the array. + */ + public function last() + { + $items = $this->getArrayCopy(); + return count($items) ? end($items) : false; + } + + /** + * Removes all `null` values from an array. + * + * @return CFArray A new object containing the non-null values. + */ + public function compress() + { + return new CFArray(array_filter($this->getArrayCopy())); + } + + /** + * Reindexes the array, starting from zero. + * + * @return CFArray A new object with indexes starting at zero. + */ + public function reindex() + { + return new CFArray(array_values($this->getArrayCopy())); + } + + + /*%******************************************************************************************%*/ + // ALTERNATE FORMATS + + /** + * Gets the current XML node as a JSON string. + * + * @return string The current XML node as a JSON string. + */ + public function to_json() + { + return json_encode($this->getArrayCopy()); + } + + /** + * Gets the current XML node as a YAML string. + * + * @return string The current XML node as a YAML string. + */ + public function to_yaml() + { + return sfYaml::dump($this->getArrayCopy(), 5); + } +} + +class CFArray_Exception extends Exception {} diff --git a/3rdparty/aws-sdk/utilities/batchrequest.class.php b/3rdparty/aws-sdk/utilities/batchrequest.class.php new file mode 100644 index 0000000000..978283471a --- /dev/null +++ b/3rdparty/aws-sdk/utilities/batchrequest.class.php @@ -0,0 +1,126 @@ +queue = array(); + $this->limit = $limit ? $limit : -1; + $this->credentials = new CFCredential(array()); + return $this; + } + + /** + * Sets the AWS credentials to use for the batch request. + * + * @param CFCredential $credentials (Required) The credentials to use for signing and making requests. + * @return $this A reference to the current instance. + */ + public function use_credentials(CFCredential $credentials) + { + $this->credentials = $credentials; + return $this; + } + + /** + * Adds a new cURL handle to the request queue. + * + * @param resource $handle (Required) A cURL resource to add to the queue. + * @return $this A reference to the current instance. + */ + public function add($handle) + { + $this->queue[] = $handle; + return $this; + } + + /** + * Executes the batch request queue. + * + * @param array $opt (DO NOT USE) Enabled for compatibility with the method this overrides, although any values passed will be ignored. + * @return array An indexed array of objects. + */ + public function send($opt = null) + { + $http = new $this->request_class(null, $this->proxy, null, $this->credentials); + + // Make the request + $response = $http->send_multi_request($this->queue, array( + 'limit' => $this->limit + )); + + return $response; + } +} diff --git a/3rdparty/aws-sdk/utilities/complextype.class.php b/3rdparty/aws-sdk/utilities/complextype.class.php new file mode 100644 index 0000000000..e0509b9e36 --- /dev/null +++ b/3rdparty/aws-sdk/utilities/complextype.class.php @@ -0,0 +1,123 @@ + function. + * @param string $member (Optional) The name of the "member" property that AWS uses for lists in certain services. Defaults to an empty string. + * @param string $default_key (Optional) The default key to use when the value for `$data` is a string. Defaults to an empty string. + * @return array The option group parameters to merge into another method's `$opt` parameter. + */ + public static function json($json, $member = '', $default_key = '') + { + return self::option_group(json_decode($json, true), $member, $default_key); + } + + /** + * Takes a YAML object, as a string, to convert to query string keys. + * + * @param string $yaml (Required) A YAML object. + * @param string $member (Optional) The name of the "member" property that AWS uses for lists in certain services. Defaults to an empty string. + * @param string $default_key (Optional) The default key to use when the value for `$data` is a string. Defaults to an empty string. + * @return array The option group parameters to merge into another method's `$opt` parameter. + */ + public static function yaml($yaml, $member = '', $default_key = '') + { + return self::option_group(sfYaml::load($yaml), $member, $default_key); + } + + /** + * Takes an associative array to convert to query string keys. + * + * @param array $map (Required) An associative array. + * @param string $member (Optional) The name of the "member" property that AWS uses for lists in certain services. Defaults to an empty string. + * @param string $default_key (Optional) The default key to use when the value for `$data` is a string. Defaults to an empty string. + * @return array The option group parameters to merge into another method's `$opt` parameter. + */ + public static function map($map, $member = '', $default_key = '') + { + return self::option_group($map, $member, $default_key); + } + + /** + * A protected method that is used by , and . + * + * @param string|array $data (Required) The data to iterate over. + * @param string $member (Optional) The name of the "member" property that AWS uses for lists in certain services. Defaults to an empty string. + * @param string $key (Optional) The default key to use when the value for `$data` is a string. Defaults to an empty string. + * @param array $out (Optional) INTERNAL ONLY. The array that contains the calculated values up to this point. + * @return array The option group parameters to merge into another method's `$opt` parameter. + */ + public static function option_group($data, $member = '', $key = '', &$out = array()) + { + $reset = $key; + + if (is_array($data)) + { + foreach ($data as $k => $v) + { + // Avoid 0-based indexes. + if (is_int($k)) + { + $k = $k + 1; + + if ($member !== '') + { + $key .= '.' . $member; + } + } + + $key .= ($key === '' ? $k : '.' . $k); + + if (is_array($v)) + { + self::option_group($v, $member, $key, $out); + } + elseif ($v instanceof CFStepConfig) + { + self::option_group($v->get_config(), $member, $key, $out); + } + else + { + $out[$key] = $v; + } + + $key = $reset; + } + } + else + { + $out[$key] = $data; + } + + return $out; + } +} diff --git a/3rdparty/aws-sdk/utilities/credential.class.php b/3rdparty/aws-sdk/utilities/credential.class.php new file mode 100644 index 0000000000..05c0ffcf6d --- /dev/null +++ b/3rdparty/aws-sdk/utilities/credential.class.php @@ -0,0 +1,157 @@ + class represents an individual credential set. + * + * @version 2011.11.15 + * @license See the included NOTICE.md file for more information. + * @copyright See the included NOTICE.md file for more information. + * @link http://aws.amazon.com/php/ PHP Developer Center + */ +class CFCredential implements ArrayAccess +{ + /** + * Stores the internal representation of the collection. + */ + private $collection; + + /** + * Default getter. Enables syntax such as $object->method->chained_method();. Also supports + * $object->key. Matching methods are prioritized over matching keys. + * + * @param string $name (Required) The name of the method to execute or key to retrieve. + * @return mixed The results of calling the function $name(), or the value of the key $object[$name]. + */ + public function __get($name) + { + return $this[$name]; + } + + /** + * Default setter. + * + * @param string $name (Required) The name of the method to execute. + * @param string $value (Required) The value to pass to the method. + * @return mixed The results of calling the function, $name. + */ + public function __set($name, $value) + { + $this[$name] = $value; + return $this; + } + + /** + * Create a clone of the object. + * + * @return CFCredential A clone of the current instance. + */ + public function __clone() + { + $this->collection = clone $this->collection; + } + + + /*%******************************************************************************************%*/ + // CONSTRUCTOR + + /** + * Constructs a new instance of the class. + */ + public function __construct($value = array()) + { + $this->collection = new ArrayObject($value, ArrayObject::ARRAY_AS_PROPS); + } + + /** + * Check whether or not a specific offset exists. + * + * @param integer $offset (Required) The location in the collection to verify the existence of. + * @return boolean A value of true indicates that the collection offset exists. A value of false indicates that it does not. + */ + public function offsetExists($offset) + { + return $this->collection->offsetExists($offset); + } + + /** + * Get the value for a specific offset. + * + * @param integer $offset (Required) The location in the collection to retrieve the value for. + * @return mixed The value of the collection offset. NULL is returned if the offset does not exist. + */ + public function offsetGet($offset) + { + if ($this->collection->offsetExists($offset)) + { + return $this->collection->offsetGet($offset); + } + + return null; + } + + /** + * Set the value for a specific offset. + * + * @param integer $offset (Required) The location in the collection to set a new value for. + * @param mixed $value (Required) The new value for the collection location. + * @return CFCredential A reference to the current collection. + */ + public function offsetSet($offset, $value) + { + $this->collection->offsetSet($offset, $value); + return $this; + } + + /** + * Unset the value for a specific offset. + * + * @param integer $offset (Required) The location in the collection to unset. + * @return CFCredential A reference to the current collection. + */ + public function offsetUnset($offset) + { + $this->collection->offsetUnset($offset); + return $this; + } + + /** + * Merge another instance of onto this one. + * + * @param CFCredential $credential (Required) Another instance of . + * @return CFCredential A reference to the current collection. + */ + public function merge(CFCredential $credential) + { + $merged = array_merge($this->to_array(), $credential->to_array()); + $this->collection->exchangeArray($merged); + return $this; + } + + /** + * Retrieves the data as a standard array. + * + * @return array The data as an array. + */ + public function to_array() + { + return $this->collection->getArrayCopy(); + } +} diff --git a/3rdparty/aws-sdk/utilities/credentials.class.php b/3rdparty/aws-sdk/utilities/credentials.class.php new file mode 100644 index 0000000000..2504a081b2 --- /dev/null +++ b/3rdparty/aws-sdk/utilities/credentials.class.php @@ -0,0 +1,125 @@ + class enables developers to easily switch between multiple sets of credentials. + * + * @version 2011.11.15 + * @license See the included NOTICE.md file for more information. + * @copyright See the included NOTICE.md file for more information. + * @link http://aws.amazon.com/php/ PHP Developer Center + */ +class CFCredentials +{ + /** + * The key used to specify the default credential set + */ + const DEFAULT_KEY = '@default'; + + /** + * The key used to identify inherited credentials + */ + const INHERIT_KEY = '@inherit'; + + /** + * Stores the credentials + */ + protected static $credentials = array(); + + /** + * Prevents this class from being constructed + */ + final private function __construct() {} + + /** + * Stores the credentials for re-use. + * + * @param array $credential_sets (Required) The named credential sets that should be made available to the application. + * @return void + */ + public static function set(array $credential_sets) + { + // Make sure a default credential set is specified or can be inferred + if (count($credential_sets) === 1) + { + $credential_sets[self::DEFAULT_KEY] = reset($credential_sets); + } + elseif (!isset($credential_sets[self::DEFAULT_KEY])) + { + throw new CFCredentials_Exception('If more than one credential set is provided, a default credential set (identified by the key "' . self::DEFAULT_KEY . '") must be specified.'); + } + + // Resolve any @inherit tags + foreach ($credential_sets as $credential_name => &$credential_set) + { + if (is_array($credential_set)) + { + foreach ($credential_set as $credential_key => &$credential_value) + { + if ($credential_key === self::INHERIT_KEY) + { + if (!isset($credential_sets[$credential_value])) + { + throw new CFCredentials_Exception('The credential set, "' . $credential_value . '", does not exist and cannot be inherited.'); + } + + $credential_set = array_merge($credential_sets[$credential_value], $credential_set); + unset($credential_set[self::INHERIT_KEY]); + } + } + } + } + + // Normalize the value of the @default credential set + $default = $credential_sets[self::DEFAULT_KEY]; + if (is_string($default)) + { + if (!isset($credential_sets[$default])) + { + throw new CFCredentials_Exception('The credential set, "' . $default . '", does not exist and cannot be used as the default credential set.'); + } + + $credential_sets[self::DEFAULT_KEY] = $credential_sets[$default]; + } + + // Store the credentials + self::$credentials = $credential_sets; + } + + /** + * Retrieves the requested credentials from the internal credential store. + * + * @param string $credential_set (Optional) The name of the credential set to retrieve. The default value is set in DEFAULT_KEY. + * @return stdClass A stdClass object where the properties represent the keys that were provided. + */ + public static function get($credential_name = self::DEFAULT_KEY) + { + // Make sure the credential set exists + if (!isset(self::$credentials[$credential_name])) + { + throw new CFCredentials_Exception('The credential set, "' . $credential_name . '", does not exist and cannot be retrieved.'); + } + + // Return the credential set as an object + return new CFCredential(self::$credentials[$credential_name]); + } +} + +class CFCredentials_Exception extends Exception {} diff --git a/3rdparty/aws-sdk/utilities/gzipdecode.class.php b/3rdparty/aws-sdk/utilities/gzipdecode.class.php new file mode 100644 index 0000000000..f80822a704 --- /dev/null +++ b/3rdparty/aws-sdk/utilities/gzipdecode.class.php @@ -0,0 +1,377 @@ +compressed_data = $data; + $this->compressed_size = strlen($data); + } + + /** + * Decode the GZIP stream + * + * @access public + */ + public function parse() + { + if ($this->compressed_size >= $this->min_compressed_size) + { + // Check ID1, ID2, and CM + if (substr($this->compressed_data, 0, 3) !== "\x1F\x8B\x08") + { + return false; + } + + // Get the FLG (FLaGs) + $this->flags = ord($this->compressed_data[3]); + + // FLG bits above (1 << 4) are reserved + if ($this->flags > 0x1F) + { + return false; + } + + // Advance the pointer after the above + $this->position += 4; + + // MTIME + $mtime = substr($this->compressed_data, $this->position, 4); + // Reverse the string if we're on a big-endian arch because l is the only signed long and is machine endianness + if (current(unpack('S', "\x00\x01")) === 1) + { + $mtime = strrev($mtime); + } + $this->MTIME = current(unpack('l', $mtime)); + $this->position += 4; + + // Get the XFL (eXtra FLags) + $this->XFL = ord($this->compressed_data[$this->position++]); + + // Get the OS (Operating System) + $this->OS = ord($this->compressed_data[$this->position++]); + + // Parse the FEXTRA + if ($this->flags & 4) + { + // Read subfield IDs + $this->SI1 = $this->compressed_data[$this->position++]; + $this->SI2 = $this->compressed_data[$this->position++]; + + // SI2 set to zero is reserved for future use + if ($this->SI2 === "\x00") + { + return false; + } + + // Get the length of the extra field + $len = current(unpack('v', substr($this->compressed_data, $this->position, 2))); + $position += 2; + + // Check the length of the string is still valid + $this->min_compressed_size += $len + 4; + if ($this->compressed_size >= $this->min_compressed_size) + { + // Set the extra field to the given data + $this->extra_field = substr($this->compressed_data, $this->position, $len); + $this->position += $len; + } + else + { + return false; + } + } + + // Parse the FNAME + if ($this->flags & 8) + { + // Get the length of the filename + $len = strcspn($this->compressed_data, "\x00", $this->position); + + // Check the length of the string is still valid + $this->min_compressed_size += $len + 1; + if ($this->compressed_size >= $this->min_compressed_size) + { + // Set the original filename to the given string + $this->filename = substr($this->compressed_data, $this->position, $len); + $this->position += $len + 1; + } + else + { + return false; + } + } + + // Parse the FCOMMENT + if ($this->flags & 16) + { + // Get the length of the comment + $len = strcspn($this->compressed_data, "\x00", $this->position); + + // Check the length of the string is still valid + $this->min_compressed_size += $len + 1; + if ($this->compressed_size >= $this->min_compressed_size) + { + // Set the original comment to the given string + $this->comment = substr($this->compressed_data, $this->position, $len); + $this->position += $len + 1; + } + else + { + return false; + } + } + + // Parse the FHCRC + if ($this->flags & 2) + { + // Check the length of the string is still valid + $this->min_compressed_size += $len + 2; + if ($this->compressed_size >= $this->min_compressed_size) + { + // Read the CRC + $crc = current(unpack('v', substr($this->compressed_data, $this->position, 2))); + + // Check the CRC matches + if ((crc32(substr($this->compressed_data, 0, $this->position)) & 0xFFFF) === $crc) + { + $this->position += 2; + } + else + { + return false; + } + } + else + { + return false; + } + } + + // Decompress the actual data + if (($this->data = gzinflate(substr($this->compressed_data, $this->position, -8))) === false) + { + return false; + } + else + { + $this->position = $this->compressed_size - 8; + } + + // Check CRC of data + $crc = current(unpack('V', substr($this->compressed_data, $this->position, 4))); + $this->position += 4; + /*if (extension_loaded('hash') && sprintf('%u', current(unpack('V', hash('crc32b', $this->data)))) !== sprintf('%u', $crc)) + { + return false; + }*/ + + // Check ISIZE of data + $isize = current(unpack('V', substr($this->compressed_data, $this->position, 4))); + $this->position += 4; + if (sprintf('%u', strlen($this->data) & 0xFFFFFFFF) !== sprintf('%u', $isize)) + { + return false; + } + + // Wow, against all odds, we've actually got a valid gzip string + return true; + } + else + { + return false; + } + } +} diff --git a/3rdparty/aws-sdk/utilities/hadoopbase.class.php b/3rdparty/aws-sdk/utilities/hadoopbase.class.php new file mode 100644 index 0000000000..eb8bc5c3a8 --- /dev/null +++ b/3rdparty/aws-sdk/utilities/hadoopbase.class.php @@ -0,0 +1,67 @@ + object. + */ + public static function script_runner($script, $args = null) + { + if (!$args) $args = array(); + array_unshift($args, $script); + + return array( + 'Jar' => 's3://us-east-1.elasticmapreduce/libs/script-runner/script-runner.jar', + 'Args' => $args + ); + } + + /** + * Prepares a Hive or Pig script before passing it to the script runner. + * + * @param string $type (Required) The type of script to run. [Allowed values: `hive`, `pig`]. + * @param array $args (Optional) An indexed array of arguments to pass to the script. + * @return array A standard array that is intended to be passed into a object. + * @link http://hive.apache.org Apache Hive + * @link http://pig.apache.org Apache Pig + */ + public static function hive_pig_script($type, $args = null) + { + if (!$args) $args = array(); + $args = is_array($args) ? $args : array($args); + $args = array_merge(array('--base-path', 's3://us-east-1.elasticmapreduce/libs/' . $type . '/'), $args); + + return self::script_runner('s3://us-east-1.elasticmapreduce/libs/' . $type . '/' . $type . '-script', $args); + } +} diff --git a/3rdparty/aws-sdk/utilities/hadoopbootstrap.class.php b/3rdparty/aws-sdk/utilities/hadoopbootstrap.class.php new file mode 100644 index 0000000000..baaa0c08d4 --- /dev/null +++ b/3rdparty/aws-sdk/utilities/hadoopbootstrap.class.php @@ -0,0 +1,127 @@ +true, the bootstrap action executes. + * @param array $args (Optional) An indexed array of arguments to pass to the script. + * @return array A configuration set to be provided when running a job flow. + */ + public static function run_if($condition, $args = null) + { + if (!$args) $args = array(); + $args = is_array($args) ? $args : array($args); + + return self::script_runner('s3://us-east-1.elasticmapreduce/bootstrap-actions/run-if', $args); + } + + /** + * Specify options to merge with Hadoop's default configuration. + * + * @param string $file (Required) The Hadoop configuration file to merge with. [Allowed values: CFHadoopBootstrap::CONFIG_SITE, CFHadoopBootstrap::CONFIG_DEFAULT, CFHadoopBootstrap::CONFIG_CORE, CFHadoopBootstrap::CONFIG_HDFS, CFHadoopBootstrap::CONFIG_MAPREDUCE] + * @param string|array $config (Required) This can either be an XML file in S3 (as s3://bucket/path), or an associative array of key-value pairs. + * @return array A configuration set to be provided when running a job flow. + */ + public static function configure($file, $config) + { + $args = array(); + $file_arg = '-' . $file; + + if (is_string($config)) + { + $args[] = $file_arg; + $args[] = $config; + } + elseif (is_array($config)) + { + foreach ($config as $key => $value) + { + $args[] = $file_arg; + $args[] = $key . '=' . $value; + } + } + + return self::script_runner('s3://us-east-1.elasticmapreduce/bootstrap-actions/configure-hadoop', $args); + } + + /** + * Create a new bootstrap action which lets you configure Hadoop's daemons. The options are written to + * the hadoop-user-env.sh file. + * + * @param string $daemon_type (Required) The Hadoop daemon to configure. + * @param array $opt (Optional) An associative array of parameters that can have the following keys:
      + *
    • HeapSize - integer - Optional - The requested heap size of the daemon, in megabytes.
    • + *
    • CLIOptions - string - Optional - Additional Java command line arguments to pass to the daemon.
    • + *
    • Replace - boolean - Optional - Whether or not the file should be replaced. A value of true will replace the existing configuration file. A value of false will append the options to the configuration file.
    + * @return array A configuration set to be provided when running a job flow. + */ + public static function daemon($daemon_type, $opt = null) + { + if (!$opt) $opt = array(); + $args = array(); + + foreach ($opt as $key => $value) + { + switch ($key) + { + case 'HeapSize': + $args[] = '--' . $daemon_type . '-heap-size=' . $value; + break; + case 'CLIOptions': + $args[] = '--' . $daemon_type . '-opts="' . $value . '"'; + break; + case 'Replace': + if ((is_string($value) && $value === 'true') || (is_bool($value) && $value === true)) + { + $args[] = '--replace'; + } + break; + } + } + + return self::script_runner('s3://us-east-1.elasticmapreduce/bootstrap-actions/configure-daemons', $args); + } +} diff --git a/3rdparty/aws-sdk/utilities/hadoopstep.class.php b/3rdparty/aws-sdk/utilities/hadoopstep.class.php new file mode 100644 index 0000000000..2371e00748 --- /dev/null +++ b/3rdparty/aws-sdk/utilities/hadoopstep.class.php @@ -0,0 +1,98 @@ + object. + */ + public static function enable_debugging() + { + return self::script_runner('s3://us-east-1.elasticmapreduce/libs/state-pusher/0.1/fetch'); + } + + /** + * Step that installs Hive on your job flow. + * + * @return array A standard array that is intended to be passed into a object. + * @link http://hive.apache.org Apache Hive + */ + public static function install_hive() + { + return self::hive_pig_script('hive', '--install-hive'); + } + + /** + * Step that runs a Hive script on your job flow. + * + * @param string $script (Required) The script to run with `script-runner.jar`. + * @param array $args (Optional) An indexed array of arguments to pass to the script. + * @return array A standard array that is intended to be passed into a object. + * @link http://hive.apache.org Apache Hive + */ + public static function run_hive_script($script, $args = null) + { + if (!$args) $args = array(); + $args = is_array($args) ? $args : array($args); + $args = array_merge(array('--run-hive-script', '--args', '-f', $script), $args); + + return self::hive_pig_script('hive', $args); + } + + /** + * Step that installs Pig on your job flow. + * + * @return array A standard array that is intended to be passed into a object. + * @link http://pig.apache.org Apache Pig + */ + public static function install_pig() + { + return self::hive_pig_script('pig', '--install-pig'); + } + + /** + * Step that runs a Pig script on your job flow. + * + * @param string $script (Required) The script to run with `script-runner.jar`. + * @param array $args (Optional) An indexed array of arguments to pass to the script. + * @return array A standard array that is intended to be passed into a object. + * @link http://pig.apache.org Apache Pig + */ + public static function run_pig_script($script, $args = null) + { + if (!$args) $args = array(); + $args = is_array($args) ? $args : array($args); + $args = array_merge(array('--run-pig-script', '--args', '-f', $script), $args); + + return self::hive_pig_script('pig', $args); + } +} diff --git a/3rdparty/aws-sdk/utilities/info.class.php b/3rdparty/aws-sdk/utilities/info.class.php new file mode 100644 index 0000000000..2ba289a5f0 --- /dev/null +++ b/3rdparty/aws-sdk/utilities/info.class.php @@ -0,0 +1,69 @@ +api_version; + unset($obj); + } + + return $collect; + } +} diff --git a/3rdparty/aws-sdk/utilities/json.class.php b/3rdparty/aws-sdk/utilities/json.class.php new file mode 100644 index 0000000000..dfa8383986 --- /dev/null +++ b/3rdparty/aws-sdk/utilities/json.class.php @@ -0,0 +1,89 @@ +SimpleXMLElement. Has a default value of CFSimpleXML. + * @return CFSimpleXML An XML representation of the data. + */ + public static function to_xml($json, $parser = 'CFSimpleXML') + { + // If we haven't parsed the JSON, do it + if (!is_array($json)) + { + // Handle the case of JSON-encoded NULL value + if ($json === 'null') + { + $json = null; + } + else + { + $json = json_decode($json, true); + + if (function_exists('json_last_error')) + { + // Did we encounter an error? + switch (json_last_error()) + { + case JSON_ERROR_DEPTH: + throw new JSON_Exception('Maximum stack depth exceeded.'); + + case JSON_ERROR_CTRL_CHAR: + throw new JSON_Exception('Unexpected control character found.'); + + case JSON_ERROR_SYNTAX: + throw new JSON_Exception('Syntax error; Malformed JSON.'); + + case JSON_ERROR_STATE_MISMATCH: + throw new JSON_Exception('Invalid or malformed JSON.'); + } + } + // json_last_error() not available? + elseif ($json === null) + { + throw new JSON_Exception('Unknown JSON error. Be sure to validate your JSON and read the notes on http://php.net/json_decode.'); + } + } + } + + // Hand off for the recursive work + $string = Array2DOM::arrayToXMLString($json, 'rootElement', true); + + return simplexml_load_string($string, $parser, LIBXML_NOCDATA); + } +} + + +/** + * Default JSON Exception. + */ +class JSON_Exception extends Exception {} diff --git a/3rdparty/aws-sdk/utilities/manifest.class.php b/3rdparty/aws-sdk/utilities/manifest.class.php new file mode 100644 index 0000000000..82410a328d --- /dev/null +++ b/3rdparty/aws-sdk/utilities/manifest.class.php @@ -0,0 +1,54 @@ + function. + * @return string A YAML manifest document. + */ + public static function json($json) + { + $map = json_decode($json, true); + return sfYaml::dump($map); + } + + /** + * Takes an associative array to convert to a YAML manifest. + * + * @param array $map (Required) An associative array. + * @return string A YAML manifest document. + */ + public static function map($map) + { + return sfYaml::dump($map); + } +} diff --git a/3rdparty/aws-sdk/utilities/mimetypes.class.php b/3rdparty/aws-sdk/utilities/mimetypes.class.php new file mode 100644 index 0000000000..5fa23d3942 --- /dev/null +++ b/3rdparty/aws-sdk/utilities/mimetypes.class.php @@ -0,0 +1,223 @@ + 'video/3gpp', + 'ai' => 'application/postscript', + 'aif' => 'audio/x-aiff', + 'aifc' => 'audio/x-aiff', + 'aiff' => 'audio/x-aiff', + 'asc' => 'text/plain', + 'atom' => 'application/atom+xml', + 'au' => 'audio/basic', + 'avi' => 'video/x-msvideo', + 'bcpio' => 'application/x-bcpio', + 'bin' => 'application/octet-stream', + 'bmp' => 'image/bmp', + 'cdf' => 'application/x-netcdf', + 'cgm' => 'image/cgm', + 'class' => 'application/octet-stream', + 'cpio' => 'application/x-cpio', + 'cpt' => 'application/mac-compactpro', + 'csh' => 'application/x-csh', + 'css' => 'text/css', + 'dcr' => 'application/x-director', + 'dif' => 'video/x-dv', + 'dir' => 'application/x-director', + 'djv' => 'image/vnd.djvu', + 'djvu' => 'image/vnd.djvu', + 'dll' => 'application/octet-stream', + 'dmg' => 'application/octet-stream', + 'dms' => 'application/octet-stream', + 'doc' => 'application/msword', + 'dtd' => 'application/xml-dtd', + 'dv' => 'video/x-dv', + 'dvi' => 'application/x-dvi', + 'dxr' => 'application/x-director', + 'eps' => 'application/postscript', + 'etx' => 'text/x-setext', + 'exe' => 'application/octet-stream', + 'ez' => 'application/andrew-inset', + 'flv' => 'video/x-flv', + 'gif' => 'image/gif', + 'gram' => 'application/srgs', + 'grxml' => 'application/srgs+xml', + 'gtar' => 'application/x-gtar', + 'gz' => 'application/x-gzip', + 'hdf' => 'application/x-hdf', + 'hqx' => 'application/mac-binhex40', + 'htm' => 'text/html', + 'html' => 'text/html', + 'ice' => 'x-conference/x-cooltalk', + 'ico' => 'image/x-icon', + 'ics' => 'text/calendar', + 'ief' => 'image/ief', + 'ifb' => 'text/calendar', + 'iges' => 'model/iges', + 'igs' => 'model/iges', + 'jnlp' => 'application/x-java-jnlp-file', + 'jp2' => 'image/jp2', + 'jpe' => 'image/jpeg', + 'jpeg' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'js' => 'application/x-javascript', + 'kar' => 'audio/midi', + 'latex' => 'application/x-latex', + 'lha' => 'application/octet-stream', + 'lzh' => 'application/octet-stream', + 'm3u' => 'audio/x-mpegurl', + 'm4a' => 'audio/mp4a-latm', + 'm4p' => 'audio/mp4a-latm', + 'm4u' => 'video/vnd.mpegurl', + 'm4v' => 'video/x-m4v', + 'mac' => 'image/x-macpaint', + 'man' => 'application/x-troff-man', + 'mathml' => 'application/mathml+xml', + 'me' => 'application/x-troff-me', + 'mesh' => 'model/mesh', + 'mid' => 'audio/midi', + 'midi' => 'audio/midi', + 'mif' => 'application/vnd.mif', + 'mov' => 'video/quicktime', + 'movie' => 'video/x-sgi-movie', + 'mp2' => 'audio/mpeg', + 'mp3' => 'audio/mpeg', + 'mp4' => 'video/mp4', + 'mpe' => 'video/mpeg', + 'mpeg' => 'video/mpeg', + 'mpg' => 'video/mpeg', + 'mpga' => 'audio/mpeg', + 'ms' => 'application/x-troff-ms', + 'msh' => 'model/mesh', + 'mxu' => 'video/vnd.mpegurl', + 'nc' => 'application/x-netcdf', + 'oda' => 'application/oda', + 'ogg' => 'application/ogg', + 'ogv' => 'video/ogv', + 'pbm' => 'image/x-portable-bitmap', + 'pct' => 'image/pict', + 'pdb' => 'chemical/x-pdb', + 'pdf' => 'application/pdf', + 'pgm' => 'image/x-portable-graymap', + 'pgn' => 'application/x-chess-pgn', + 'pic' => 'image/pict', + 'pict' => 'image/pict', + 'png' => 'image/png', + 'pnm' => 'image/x-portable-anymap', + 'pnt' => 'image/x-macpaint', + 'pntg' => 'image/x-macpaint', + 'ppm' => 'image/x-portable-pixmap', + 'ppt' => 'application/vnd.ms-powerpoint', + 'ps' => 'application/postscript', + 'qt' => 'video/quicktime', + 'qti' => 'image/x-quicktime', + 'qtif' => 'image/x-quicktime', + 'ra' => 'audio/x-pn-realaudio', + 'ram' => 'audio/x-pn-realaudio', + 'ras' => 'image/x-cmu-raster', + 'rdf' => 'application/rdf+xml', + 'rgb' => 'image/x-rgb', + 'rm' => 'application/vnd.rn-realmedia', + 'roff' => 'application/x-troff', + 'rtf' => 'text/rtf', + 'rtx' => 'text/richtext', + 'sgm' => 'text/sgml', + 'sgml' => 'text/sgml', + 'sh' => 'application/x-sh', + 'shar' => 'application/x-shar', + 'silo' => 'model/mesh', + 'sit' => 'application/x-stuffit', + 'skd' => 'application/x-koan', + 'skm' => 'application/x-koan', + 'skp' => 'application/x-koan', + 'skt' => 'application/x-koan', + 'smi' => 'application/smil', + 'smil' => 'application/smil', + 'snd' => 'audio/basic', + 'so' => 'application/octet-stream', + 'spl' => 'application/x-futuresplash', + 'src' => 'application/x-wais-source', + 'sv4cpio' => 'application/x-sv4cpio', + 'sv4crc' => 'application/x-sv4crc', + 'svg' => 'image/svg+xml', + 'swf' => 'application/x-shockwave-flash', + 't' => 'application/x-troff', + 'tar' => 'application/x-tar', + 'tcl' => 'application/x-tcl', + 'tex' => 'application/x-tex', + 'texi' => 'application/x-texinfo', + 'texinfo' => 'application/x-texinfo', + 'tif' => 'image/tiff', + 'tiff' => 'image/tiff', + 'tr' => 'application/x-troff', + 'tsv' => 'text/tab-separated-values', + 'txt' => 'text/plain', + 'ustar' => 'application/x-ustar', + 'vcd' => 'application/x-cdlink', + 'vrml' => 'model/vrml', + 'vxml' => 'application/voicexml+xml', + 'wav' => 'audio/x-wav', + 'wbmp' => 'image/vnd.wap.wbmp', + 'wbxml' => 'application/vnd.wap.wbxml', + 'webm' => 'video/webm', + 'wml' => 'text/vnd.wap.wml', + 'wmlc' => 'application/vnd.wap.wmlc', + 'wmls' => 'text/vnd.wap.wmlscript', + 'wmlsc' => 'application/vnd.wap.wmlscriptc', + 'wmv' => 'video/x-ms-wmv', + 'wrl' => 'model/vrml', + 'xbm' => 'image/x-xbitmap', + 'xht' => 'application/xhtml+xml', + 'xhtml' => 'application/xhtml+xml', + 'xls' => 'application/vnd.ms-excel', + 'xml' => 'application/xml', + 'xpm' => 'image/x-xpixmap', + 'xsl' => 'application/xml', + 'xslt' => 'application/xslt+xml', + 'xul' => 'application/vnd.mozilla.xul+xml', + 'xwd' => 'image/x-xwindowdump', + 'xyz' => 'chemical/x-xyz', + 'zip' => 'application/zip', + ); + + /** + * Attempt to match the file extension to a known mime-type. + * + * @param string $ext (Required) The file extension to attempt to map. + * @return string The mime-type to use for the file extension. + */ + public static function get_mimetype($ext) + { + return (isset(self::$mime_types[$ext]) ? self::$mime_types[$ext] : 'application/octet-stream'); + } +} diff --git a/3rdparty/aws-sdk/utilities/policy.class.php b/3rdparty/aws-sdk/utilities/policy.class.php new file mode 100644 index 0000000000..9c53fee0d7 --- /dev/null +++ b/3rdparty/aws-sdk/utilities/policy.class.php @@ -0,0 +1,134 @@ + (e.g. , ). + * @param string|array $policy (Required) The associative array representing the S3 policy to use, or a string of JSON content. + * @return $this A reference to the current instance. + * @link http://docs.amazonwebservices.com/AmazonS3/2006-03-01/dev/index.html?HTTPPOSTForms.html S3 Policies + * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/index.html?AccessPolicyLanguage.html Access Policy Language + */ + public function __construct($auth, $policy) + { + $this->auth = $auth; + + if (is_array($policy)) // We received an associative array... + { + $this->json_policy = json_encode($policy); + } + else // We received a valid, parseable JSON string... + { + $this->json_policy = json_encode(json_decode($policy, true)); + } + + return $this; + } + + /** + * Alternate approach to constructing a new instance. Supports chaining. + * + * @param CFRuntime $auth (Required) An instance of any authenticated AWS object that is an instance of (e.g. , ). + * @param string|array $policy (Required) The associative array representing the S3 policy to use, or a string of JSON content. + * @return $this A reference to the current instance. + */ + public static function init($auth, $policy) + { + if (version_compare(PHP_VERSION, '5.3.0', '<')) + { + throw new Exception('PHP 5.3 or newer is required to instantiate a new class with CLASS::init().'); + } + + $self = get_called_class(); + return new $self($auth, $policy); + } + + /** + * Get the key from the authenticated instance. + * + * @return string The key from the authenticated instance. + */ + public function get_key() + { + return $this->auth->key; + } + + /** + * Base64-encodes the JSON string. + * + * @return string The Base64-encoded version of the JSON string. + */ + public function get_policy() + { + return base64_encode($this->json_policy); + } + + /** + * Gets the JSON string with the whitespace removed. + * + * @return string The JSON string without extraneous whitespace. + */ + public function get_json() + { + return $this->json_policy; + } + + /** + * Gets the JSON string with the whitespace removed. + * + * @return string The Base64-encoded, signed JSON string. + */ + public function get_policy_signature() + { + return base64_encode(hash_hmac('sha1', $this->get_policy(), $this->auth->secret_key)); + } + + /** + * Decode a policy that was returned from the service. + * + * @param string $response (Required) The policy returned by AWS that you want to decode into an object. + * @return string The Base64-encoded, signed JSON string. + */ + public static function decode_policy($response) + { + return json_decode(urldecode($response), true); + } +} diff --git a/3rdparty/aws-sdk/utilities/request.class.php b/3rdparty/aws-sdk/utilities/request.class.php new file mode 100644 index 0000000000..8e049fb516 --- /dev/null +++ b/3rdparty/aws-sdk/utilities/request.class.php @@ -0,0 +1,70 @@ +). + */ + public $request_class = 'CFRequest'; + + /** + * The default class to use for HTTP Responses (defaults to ). + */ + public $response_class = 'CFResponse'; + + /** + * The active credential set. + */ + public $credentials; + + + /*%******************************************************************************************%*/ + // CONSTRUCTOR + + /** + * Constructs a new instance of this class. + * + * @param string $url (Optional) The URL to request or service endpoint to query. + * @param string $proxy (Optional) The faux-url to use for proxy settings. Takes the following format: `proxy://user:pass@hostname:port` + * @param array $helpers (Optional) An associative array of classnames to use for request, and response functionality. Gets passed in automatically by the calling class. + * @param CFCredential $credentials (Required) The credentials to use for signing and making requests. + * @return $this A reference to the current instance. + */ + public function __construct($url = null, $proxy = null, $helpers = null, CFCredential $credentials = null) + { + parent::__construct($url, $proxy, $helpers); + + // Standard settings for all requests + $this->set_useragent(CFRUNTIME_USERAGENT); + $this->credentials = $credentials; + $this->cacert_location = ($this->credentials['certificate_authority'] ? $this->credentials['certificate_authority'] : false); + + return $this; + } +} diff --git a/3rdparty/aws-sdk/utilities/response.class.php b/3rdparty/aws-sdk/utilities/response.class.php new file mode 100644 index 0000000000..740d55d5d9 --- /dev/null +++ b/3rdparty/aws-sdk/utilities/response.class.php @@ -0,0 +1,29 @@ + element. + */ + public function __call($name, $arguments) + { + // Remap $this + $self = $this; + + // Re-base the XML + $self = new CFSimpleXML($self->asXML()); + + // Determine XPath query + $self->xpath_expression = 'descendant-or-self::' . $name; + + // Get the results and augment with CFArray + $results = $self->xpath($self->xpath_expression); + if (!count($results)) return false; + $results = new CFArray($results); + + // If an integer was passed, return only that result + if (isset($arguments[0]) && is_int($arguments[0])) + { + if (isset($results[$arguments[0]])) + { + return $results[$arguments[0]]; + } + + return false; + } + + return $results; + } + + /** + * Alternate approach to constructing a new instance. Supports chaining. + * + * @param string $data (Required) A well-formed XML string or the path or URL to an XML document if $data_is_url is true. + * @param integer $options (Optional) Used to specify additional LibXML parameters. The default value is 0. + * @param boolean $data_is_url (Optional) Specify a value of true to specify that data is a path or URL to an XML document instead of string data. The default value is false. + * @param string $ns (Optional) The XML namespace to return values for. + * @param boolean $is_prefix (Optional) (No description provided by PHP.net.) + * @return CFSimpleXML Creates a new element. + */ + public static function init($data, $options = 0, $data_is_url, $ns, $is_prefix = false) + { + if (version_compare(PHP_VERSION, '5.3.0', '<')) + { + throw new Exception('PHP 5.3 or newer is required to instantiate a new class with CLASS::init().'); + } + + $self = get_called_class(); + return new $self($data, $options, $data_is_url, $ns, $is_prefix); + } + + + /*%******************************************************************************************%*/ + // TRAVERSAL + + /** + * Wraps the results of an XPath query in a object. + * + * @param string $expr (Required) The XPath expression to use to query the XML response. + * @return CFArray A object containing the results of the XPath query. + */ + public function query($expr) + { + return new CFArray($this->xpath($expr)); + } + + /** + * Gets the parent or a preferred ancestor of the current element. + * + * @param string $node (Optional) Name of the ancestor element to match and return. + * @return CFSimpleXML A object containing the requested node. + */ + public function parent($node = null) + { + if ($node) + { + $parents = $this->xpath('ancestor-or-self::' . $node); + } + else + { + $parents = $this->xpath('parent::*'); + } + + return $parents[0]; + } + + + /*%******************************************************************************************%*/ + // ALTERNATE FORMATS + + /** + * Gets the current XML node as a true string. + * + * @return string The current XML node as a true string. + */ + public function to_string() + { + return (string) $this; + } + + /** + * Gets the current XML node as , a child class of PHP's class. + * + * @return CFArray The current XML node as a object. + */ + public function to_array() + { + return new CFArray(json_decode(json_encode($this), true)); + } + + /** + * Gets the current XML node as a stdClass object. + * + * @return array The current XML node as a stdClass object. + */ + public function to_stdClass() + { + return json_decode(json_encode($this)); + } + + /** + * Gets the current XML node as a JSON string. + * + * @return string The current XML node as a JSON string. + */ + public function to_json() + { + return json_encode($this); + } + + /** + * Gets the current XML node as a YAML string. + * + * @return string The current XML node as a YAML string. + */ + public function to_yaml() + { + return sfYaml::dump(json_decode(json_encode($this), true), 5); + } + + + /*%******************************************************************************************%*/ + // COMPARISONS + + /** + * Whether or not the current node exactly matches the compared value. + * + * @param string $value (Required) The value to compare the current node to. + * @return boolean Whether or not the current node exactly matches the compared value. + */ + public function is($value) + { + return ((string) $this === $value); + } + + /** + * Whether or not the current node contains the compared value. + * + * @param string $value (Required) The value to use to determine whether it is contained within the node. + * @return boolean Whether or not the current node contains the compared value. + */ + public function contains($value) + { + return (stripos((string) $this, $value) !== false); + } + + /** + * Whether or not the current node matches the regular expression pattern. + * + * @param string $pattern (Required) The pattern to match the current node against. + * @return boolean Whether or not the current node matches the pattern. + */ + public function matches($pattern) + { + return (bool) preg_match($pattern, (string) $this); + } + + /** + * Whether or not the current node starts with the compared value. + * + * @param string $value (Required) The value to compare the current node to. + * @return boolean Whether or not the current node starts with the compared value. + */ + public function starts_with($value) + { + return $this->matches("@^$value@u"); + } + + /** + * Whether or not the current node ends with the compared value. + * + * @param string $value (Required) The value to compare the current node to. + * @return boolean Whether or not the current node ends with the compared value. + */ + public function ends_with($value) + { + return $this->matches("@$value$@u"); + } +} diff --git a/3rdparty/aws-sdk/utilities/stacktemplate.class.php b/3rdparty/aws-sdk/utilities/stacktemplate.class.php new file mode 100644 index 0000000000..1e29ef3403 --- /dev/null +++ b/3rdparty/aws-sdk/utilities/stacktemplate.class.php @@ -0,0 +1,52 @@ +strict JSON-specific formatting. + * @return string A JSON representation of the template. + */ + public static function json($template) + { + return json_encode(json_decode($template, true)); + } + + /** + * Converts an associative array (map) of the template into a JSON string. + * + * @param array $template (Required) An associative array that maps directly to its JSON counterpart. + * @return string A JSON representation of the template. + */ + public static function map($template) + { + return json_encode($template); + } +} diff --git a/3rdparty/aws-sdk/utilities/stepconfig.class.php b/3rdparty/aws-sdk/utilities/stepconfig.class.php new file mode 100644 index 0000000000..71492995f4 --- /dev/null +++ b/3rdparty/aws-sdk/utilities/stepconfig.class.php @@ -0,0 +1,91 @@ +config = $config; + } + + /** + * Constructs a new instance of this class, and allows chaining. + * + * @param array $config (Required) An associative array representing the Hadoop step configuration. + * @return $this A reference to the current instance. + */ + public static function init($config) + { + if (version_compare(PHP_VERSION, '5.3.0', '<')) + { + throw new Exception('PHP 5.3 or newer is required to instantiate a new class with CLASS::init().'); + } + + $self = get_called_class(); + return new $self($config); + } + + /** + * Returns a JSON representation of the object when typecast as a string. + * + * @return string A JSON representation of the object. + * @link http://www.php.net/manual/en/language.oop5.magic.php#language.oop5.magic.tostring PHP Magic Methods + */ + public function __toString() + { + return json_encode($this->config); + } + + /** + * Returns the configuration data. + * + * @return array The configuration data. + */ + public function get_config() + { + return $this->config; + } +} diff --git a/3rdparty/aws-sdk/utilities/utilities.class.php b/3rdparty/aws-sdk/utilities/utilities.class.php new file mode 100755 index 0000000000..d4d1120abf --- /dev/null +++ b/3rdparty/aws-sdk/utilities/utilities.class.php @@ -0,0 +1,399 @@ +getConstant($const); + } + + /** + * Convert a HEX value to Base64. + * + * @param string $str (Required) Value to convert. + * @return string Base64-encoded string. + */ + public function hex_to_base64($str) + { + $raw = ''; + + for ($i = 0; $i < strlen($str); $i += 2) + { + $raw .= chr(hexdec(substr($str, $i, 2))); + } + + return base64_encode($raw); + } + + /** + * Convert an associative array into a query string. + * + * @param array $array (Required) Array to convert. + * @return string URL-friendly query string. + */ + public function to_query_string($array) + { + $temp = array(); + + foreach ($array as $key => $value) + { + if (is_string($key) && !is_array($value)) + { + $temp[] = rawurlencode($key) . '=' . rawurlencode($value); + } + } + + return implode('&', $temp); + } + + /** + * Convert an associative array into a sign-able string. + * + * @param array $array (Required) Array to convert. + * @return string URL-friendly sign-able string. + */ + public function to_signable_string($array) + { + $t = array(); + + foreach ($array as $k => $v) + { + $t[] = $this->encode_signature2($k) . '=' . $this->encode_signature2($v); + } + + return implode('&', $t); + } + + /** + * Encode the value according to RFC 3986. + * + * @param string $string (Required) String to convert. + * @return string URL-friendly sign-able string. + */ + public function encode_signature2($string) + { + $string = rawurlencode($string); + return str_replace('%7E', '~', $string); + } + + /** + * Convert a query string into an associative array. Multiple, identical keys will become an indexed array. + * + * @param string $qs (Required) Query string to convert. + * @return array Associative array of keys and values. + */ + public function query_to_array($qs) + { + $query = explode('&', $qs); + $data = array(); + + foreach ($query as $q) + { + $q = explode('=', $q); + + if (isset($data[$q[0]]) && is_array($data[$q[0]])) + { + $data[$q[0]][] = urldecode($q[1]); + } + else if (isset($data[$q[0]]) && !is_array($data[$q[0]])) + { + $data[$q[0]] = array($data[$q[0]]); + $data[$q[0]][] = urldecode($q[1]); + } + else + { + $data[urldecode($q[0])] = urldecode($q[1]); + } + } + return $data; + } + + /** + * Return human readable file sizes. + * + * @author Aidan Lister + * @author Ryan Parman + * @license http://www.php.net/license/3_01.txt PHP License + * @param integer $size (Required) Filesize in bytes. + * @param string $unit (Optional) The maximum unit to use. Defaults to the largest appropriate unit. + * @param string $default (Optional) The format for the return string. Defaults to `%01.2f %s`. + * @return string The human-readable file size. + * @link http://aidanlister.com/repos/v/function.size_readable.php Original Function + */ + public function size_readable($size, $unit = null, $default = null) + { + // Units + $sizes = array('B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB'); + $mod = 1024; + $ii = count($sizes) - 1; + + // Max unit + $unit = array_search((string) $unit, $sizes); + if ($unit === null || $unit === false) + { + $unit = $ii; + } + + // Return string + if ($default === null) + { + $default = '%01.2f %s'; + } + + // Loop + $i = 0; + while ($unit != $i && $size >= 1024 && $i < $ii) + { + $size /= $mod; + $i++; + } + + return sprintf($default, $size, $sizes[$i]); + } + + /** + * Convert a number of seconds into Hours:Minutes:Seconds. + * + * @param integer $seconds (Required) The number of seconds to convert. + * @return string The formatted time. + */ + public function time_hms($seconds) + { + $time = ''; + + // First pass + $hours = (int) ($seconds / 3600); + $seconds = $seconds % 3600; + $minutes = (int) ($seconds / 60); + $seconds = $seconds % 60; + + // Cleanup + $time .= ($hours) ? $hours . ':' : ''; + $time .= ($minutes < 10 && $hours > 0) ? '0' . $minutes : $minutes; + $time .= ':'; + $time .= ($seconds < 10) ? '0' . $seconds : $seconds; + + return $time; + } + + /** + * Returns the first value that is set. Based on [Try.these()](http://api.prototypejs.org/language/Try/these/) from [Prototype](http://prototypejs.org). + * + * @param array $attrs (Required) The attributes to test, as strings. Intended for testing properties of the $base object, but also works with variables if you place an @ symbol at the beginning of the command. + * @param object $base (Optional) The base object to use, if any. + * @param mixed $default (Optional) What to return if there are no matches. Defaults to `null`. + * @return mixed Either a matching property of a given object, boolean `false`, or any other data type you might choose. + */ + public function try_these($attrs, $base = null, $default = null) + { + if ($base) + { + foreach ($attrs as $attr) + { + if (isset($base->$attr)) + { + return $base->$attr; + } + } + } + else + { + foreach ($attrs as $attr) + { + if (isset($attr)) + { + return $attr; + } + } + } + + return $default; + } + + /** + * Can be removed once all calls are updated. + * + * @deprecated Use instead. + * @param mixed $obj (Required) The PHP object to convert into a JSON string. + * @return string A JSON string. + */ + public function json_encode($obj) + { + return json_encode($obj); + } + + /** + * Converts a SimpleXML response to an array structure. + * + * @param ResponseCore $response (Required) A response value. + * @return array The response value as a standard, multi-dimensional array. + */ + public function convert_response_to_array(ResponseCore $response) + { + return json_decode(json_encode($response), true); + } + + /** + * Checks to see if a date stamp is ISO-8601 formatted, and if not, makes it so. + * + * @param string $datestamp (Required) A date stamp, or a string that can be parsed into a date stamp. + * @return string An ISO-8601 formatted date stamp. + */ + public function convert_date_to_iso8601($datestamp) + { + if (!preg_match('/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}((\+|-)\d{2}:\d{2}|Z)/m', $datestamp)) + { + return gmdate(self::DATE_FORMAT_ISO8601, strtotime($datestamp)); + } + + return $datestamp; + } + + /** + * Determines whether the data is Base64 encoded or not. + * + * @license http://us.php.net/manual/en/function.base64-decode.php#81425 PHP License + * @param string $s (Required) The string to test. + * @return boolean Whether the string is Base64 encoded or not. + */ + public function is_base64($s) + { + return (bool) preg_match('/^[a-zA-Z0-9\/\r\n+]*={0,2}$/', $s); + } + + /** + * Determines whether the data is a JSON string or not. + * + * @param string $s (Required) The string to test. + * @return boolean Whether the string is a valid JSON object or not. + */ + public function is_json($s) + { + return !!(json_decode($s) instanceof stdClass); + } + + /** + * Decodes `\uXXXX` entities into their real unicode character equivalents. + * + * @param string $s (Required) The string to decode. + * @return string The decoded string. + */ + public function decode_uhex($s) + { + preg_match_all('/\\\u([0-9a-f]{4})/i', $s, $matches); + $matches = $matches[count($matches) - 1]; + $map = array(); + + foreach ($matches as $match) + { + if (!isset($map[$match])) + { + $map['\u' . $match] = html_entity_decode('&#' . hexdec($match) . ';', ENT_NOQUOTES, 'UTF-8'); + } + } + + return str_replace(array_keys($map), $map, $s); + } + + /** + * Generates a random GUID. + * + * @author Alix Axel + * @license http://www.php.net/license/3_01.txt PHP License + * @return string A random GUID. + */ + public function generate_guid() + { + return sprintf( + '%04X%04X-%04X-%04X-%04X-%04X%04X%04X', + mt_rand(0, 65535), + mt_rand(0, 65535), + mt_rand(0, 65535), + mt_rand(16384, 20479), + mt_rand(32768, 49151), + mt_rand(0, 65535), + mt_rand(0, 65535), + mt_rand(0, 65535) + ); + } +} diff --git a/3rdparty/mediawiki/CSSMin.php b/3rdparty/mediawiki/CSSMin.php new file mode 100644 index 0000000000..e9c2badf62 --- /dev/null +++ b/3rdparty/mediawiki/CSSMin.php @@ -0,0 +1,228 @@ + + * @copyright Copyright 2010 Wikimedia Foundation + * @license http://www.apache.org/licenses/LICENSE-2.0 + */ + +/** + * Transforms CSS data + * + * This class provides minification, URL remapping, URL extracting, and data-URL embedding. + */ +class CSSMin { + + /* Constants */ + + /** + * Maximum file size to still qualify for in-line embedding as a data-URI + * + * 24,576 is used because Internet Explorer has a 32,768 byte limit for data URIs, + * which when base64 encoded will result in a 1/3 increase in size. + */ + const EMBED_SIZE_LIMIT = 24576; + const URL_REGEX = 'url\(\s*[\'"]?(?P[^\?\)\'"]*)(?P\??[^\)\'"]*)[\'"]?\s*\)'; + + /* Protected Static Members */ + + /** @var array List of common image files extensions and mime-types */ + protected static $mimeTypes = array( + 'gif' => 'image/gif', + 'jpe' => 'image/jpeg', + 'jpeg' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'png' => 'image/png', + 'tif' => 'image/tiff', + 'tiff' => 'image/tiff', + 'xbm' => 'image/x-xbitmap', + ); + + /* Static Methods */ + + /** + * Gets a list of local file paths which are referenced in a CSS style sheet + * + * @param $source string CSS data to remap + * @param $path string File path where the source was read from (optional) + * @return array List of local file references + */ + public static function getLocalFileReferences( $source, $path = null ) { + $files = array(); + $rFlags = PREG_OFFSET_CAPTURE | PREG_SET_ORDER; + if ( preg_match_all( '/' . self::URL_REGEX . '/', $source, $matches, $rFlags ) ) { + foreach ( $matches as $match ) { + $file = ( isset( $path ) + ? rtrim( $path, '/' ) . '/' + : '' ) . "{$match['file'][0]}"; + + // Only proceed if we can access the file + if ( !is_null( $path ) && file_exists( $file ) ) { + $files[] = $file; + } + } + } + return $files; + } + + /** + * @param $file string + * @return bool|string + */ + protected static function getMimeType( $file ) { + $realpath = realpath( $file ); + // Try a couple of different ways to get the mime-type of a file, in order of + // preference + if ( + $realpath + && function_exists( 'finfo_file' ) + && function_exists( 'finfo_open' ) + && defined( 'FILEINFO_MIME_TYPE' ) + ) { + // As of PHP 5.3, this is how you get the mime-type of a file; it uses the Fileinfo + // PECL extension + return finfo_file( finfo_open( FILEINFO_MIME_TYPE ), $realpath ); + } elseif ( function_exists( 'mime_content_type' ) ) { + // Before this was deprecated in PHP 5.3, this was how you got the mime-type of a file + return mime_content_type( $file ); + } else { + // Worst-case scenario has happened, use the file extension to infer the mime-type + $ext = strtolower( pathinfo( $file, PATHINFO_EXTENSION ) ); + if ( isset( self::$mimeTypes[$ext] ) ) { + return self::$mimeTypes[$ext]; + } + } + return false; + } + + /** + * Remaps CSS URL paths and automatically embeds data URIs for URL rules + * preceded by an /* @embed * / comment + * + * @param $source string CSS data to remap + * @param $local string File path where the source was read from + * @param $remote string URL path to the file + * @param $embedData bool If false, never do any data URI embedding, even if / * @embed * / is found + * @return string Remapped CSS data + */ + public static function remap( $source, $local, $remote, $embedData = true ) { + $pattern = '/((?P\s*\/\*\s*\@embed\s*\*\/)(?P
    [^\;\}]*))?' .
    +			self::URL_REGEX . '(?P[^;]*)[\;]?/';
    +		$offset = 0;
    +		while ( preg_match( $pattern, $source, $match, PREG_OFFSET_CAPTURE, $offset ) ) {
    +			// Skip fully-qualified URLs and data URIs
    +			$urlScheme = parse_url( $match['file'][0], PHP_URL_SCHEME );
    +			if ( $urlScheme ) {
    +				// Move the offset to the end of the match, leaving it alone
    +				$offset = $match[0][1] + strlen( $match[0][0] );
    +				continue;
    +			}
    +			// URLs with absolute paths like /w/index.php need to be expanded
    +			// to absolute URLs but otherwise left alone
    +			if ( $match['file'][0] !== '' && $match['file'][0][0] === '/' ) {
    +				// Replace the file path with an expanded (possibly protocol-relative) URL
    +				// ...but only if wfExpandUrl() is even available.
    +				// This will not be the case if we're running outside of MW
    +				$lengthIncrease = 0;
    +				if ( function_exists( 'wfExpandUrl' ) ) {
    +					$expanded = wfExpandUrl( $match['file'][0], PROTO_RELATIVE );
    +					$origLength = strlen( $match['file'][0] );
    +					$lengthIncrease = strlen( $expanded ) - $origLength;
    +					$source = substr_replace( $source, $expanded,
    +						$match['file'][1], $origLength
    +					);
    +				}
    +				// Move the offset to the end of the match, leaving it alone
    +				$offset = $match[0][1] + strlen( $match[0][0] ) + $lengthIncrease;
    +				continue;
    +			}
    +			// Shortcuts
    +			$embed = $match['embed'][0];
    +			$pre = $match['pre'][0];
    +			$post = $match['post'][0];
    +			$query = $match['query'][0];
    +			$url = "{$remote}/{$match['file'][0]}";
    +			$file = "{$local}/{$match['file'][0]}";
    +			// bug 27052 - Guard against double slashes, because foo//../bar
    +			// apparently resolves to foo/bar on (some?) clients
    +			$url = preg_replace( '#([^:])//+#', '\1/', $url );
    +			$replacement = false;
    +			if ( $local !== false && file_exists( $file ) ) {
    +				// Add version parameter as a time-stamp in ISO 8601 format,
    +				// using Z for the timezone, meaning GMT
    +				$url .= '?' . gmdate( 'Y-m-d\TH:i:s\Z', round( filemtime( $file ), -2 ) );
    +				// Embedding requires a bit of extra processing, so let's skip that if we can
    +				if ( $embedData && $embed ) {
    +					$type = self::getMimeType( $file );
    +					// Detect when URLs were preceeded with embed tags, and also verify file size is
    +					// below the limit
    +					if (
    +						$type
    +						&& $match['embed'][1] > 0
    +						&& filesize( $file ) < self::EMBED_SIZE_LIMIT
    +					) {
    +						// Strip off any trailing = symbols (makes browsers freak out)
    +						$data = base64_encode( file_get_contents( $file ) );
    +						// Build 2 CSS properties; one which uses a base64 encoded data URI in place
    +						// of the @embed comment to try and retain line-number integrity, and the
    +						// other with a remapped an versioned URL and an Internet Explorer hack
    +						// making it ignored in all browsers that support data URIs
    +						$replacement = "{$pre}url(data:{$type};base64,{$data}){$post};";
    +						$replacement .= "{$pre}url({$url}){$post}!ie;";
    +					}
    +				}
    +				if ( $replacement === false ) {
    +					// Assume that all paths are relative to $remote, and make them absolute
    +					$replacement = "{$embed}{$pre}url({$url}){$post};";
    +				}
    +			} elseif ( $local === false ) {
    +				// Assume that all paths are relative to $remote, and make them absolute
    +				$replacement = "{$embed}{$pre}url({$url}{$query}){$post};";
    +			}
    +			if ( $replacement !== false ) {
    +				// Perform replacement on the source
    +				$source = substr_replace(
    +					$source, $replacement, $match[0][1], strlen( $match[0][0] )
    +				);
    +				// Move the offset to the end of the replacement in the source
    +				$offset = $match[0][1] + strlen( $replacement );
    +				continue;
    +			}
    +			// Move the offset to the end of the match, leaving it alone
    +			$offset = $match[0][1] + strlen( $match[0][0] );
    +		}
    +		return $source;
    +	}
    +
    +	/**
    +	 * Removes whitespace from CSS data
    +	 *
    +	 * @param $css string CSS data to minify
    +	 * @return string Minified CSS data
    +	 */
    +	public static function minify( $css ) {
    +		return trim(
    +			str_replace(
    +				array( '; ', ': ', ' {', '{ ', ', ', '} ', ';}' ),
    +				array( ';', ':', '{', '{', ',', '}', '}' ),
    +				preg_replace( array( '/\s+/', '/\/\*.*?\*\//s' ), array( ' ', '' ), $css )
    +			)
    +		);
    +	}
    +}
    diff --git a/3rdparty/mediawiki/JavaScriptMinifier.php b/3rdparty/mediawiki/JavaScriptMinifier.php
    new file mode 100644
    index 0000000000..db5326c7cf
    --- /dev/null
    +++ b/3rdparty/mediawiki/JavaScriptMinifier.php
    @@ -0,0 +1,606 @@
    +
    + * @license Choose any of Apache, MIT, GPL, LGPL
    + */
    +
    +/**
    + * This class is meant to safely minify javascript code, while leaving syntactically correct
    + * programs intact. Other libraries, such as JSMin require a certain coding style to work
    + * correctly. OTOH, libraries like jsminplus, that do parse the code correctly are rather
    + * slow, because they construct a complete parse tree before outputting the code minified.
    + * So this class is meant to allow arbitrary (but syntactically correct) input, while being
    + * fast enough to be used for on-the-fly minifying.
    + */
    +class JavaScriptMinifier {
    +
    +	/* Class constants */
    +	/* Parsing states.
    +	 * The state machine is only necessary to decide whether to parse a slash as division
    +	 * operator or as regexp literal.
    +	 * States are named after the next expected item. We only distinguish states when the
    +	 * distinction is relevant for our purpose.
    +	 */
    +	const STATEMENT                = 0;
    +	const CONDITION                = 1;
    +	const PROPERTY_ASSIGNMENT      = 2;
    +	const EXPRESSION               = 3;
    +	const EXPRESSION_NO_NL         = 4; // only relevant for semicolon insertion
    +	const EXPRESSION_OP            = 5;
    +	const EXPRESSION_FUNC          = 6;
    +	const EXPRESSION_TERNARY       = 7; // used to determine the role of a colon
    +	const EXPRESSION_TERNARY_OP    = 8;
    +	const EXPRESSION_TERNARY_FUNC  = 9;
    +	const PAREN_EXPRESSION         = 10; // expression which is not on the top level
    +	const PAREN_EXPRESSION_OP      = 11;
    +	const PAREN_EXPRESSION_FUNC    = 12;
    +	const PROPERTY_EXPRESSION      = 13; // expression which is within an object literal
    +	const PROPERTY_EXPRESSION_OP   = 14;
    +	const PROPERTY_EXPRESSION_FUNC = 15;
    +
    +	/* Token types */
    +	const TYPE_UN_OP       = 1; // unary operators
    +	const TYPE_INCR_OP     = 2; // ++ and --
    +	const TYPE_BIN_OP      = 3; // binary operators
    +	const TYPE_ADD_OP      = 4; // + and - which can be either unary or binary ops
    +	const TYPE_HOOK        = 5; // ?
    +	const TYPE_COLON       = 6; // :
    +	const TYPE_COMMA       = 7; // ,
    +	const TYPE_SEMICOLON   = 8; // ;
    +	const TYPE_BRACE_OPEN  = 9; // {
    +	const TYPE_BRACE_CLOSE = 10; // }
    +	const TYPE_PAREN_OPEN  = 11; // ( and [
    +	const TYPE_PAREN_CLOSE = 12; // ) and ]
    +	const TYPE_RETURN      = 13; // keywords: break, continue, return, throw
    +	const TYPE_IF          = 14; // keywords: catch, for, with, switch, while, if
    +	const TYPE_DO          = 15; // keywords: case, var, finally, else, do, try
    +	const TYPE_FUNC        = 16; // keywords: function
    +	const TYPE_LITERAL     = 17; // all literals, identifiers and unrecognised tokens
    +
    +	// Sanity limit to avoid excessive memory usage
    +	const STACK_LIMIT = 1000;
    +
    +	/* Static functions */
    +
    +	/**
    +	 * Returns minified JavaScript code.
    +	 *
    +	 * NOTE: $maxLineLength isn't a strict maximum. Longer lines will be produced when
    +	 *       literals (e.g. quoted strings) longer than $maxLineLength are encountered
    +	 *       or when required to guard against semicolon insertion.
    +	 *
    +	 * @param $s String JavaScript code to minify
    +	 * @param $statementsOnOwnLine Bool Whether to put each statement on its own line
    +	 * @param $maxLineLength Int Maximum length of a single line, or -1 for no maximum.
    +	 * @return String Minified code
    +	 */
    +	public static function minify( $s, $statementsOnOwnLine = false, $maxLineLength = 1000 ) {
    +		// First we declare a few tables that contain our parsing rules
    +
    +		// $opChars : characters, which can be combined without whitespace in between them
    +		$opChars = array(
    +			'!' => true,
    +			'"' => true,
    +			'%' => true,
    +			'&' => true,
    +			"'" => true,
    +			'(' => true,
    +			')' => true,
    +			'*' => true,
    +			'+' => true,
    +			',' => true,
    +			'-' => true,
    +			'.' => true,
    +			'/' => true,
    +			':' => true,
    +			';' => true,
    +			'<' => true,
    +			'=' => true,
    +			'>' => true,
    +			'?' => true,
    +			'[' => true,
    +			']' => true,
    +			'^' => true,
    +			'{' => true,
    +			'|' => true,
    +			'}' => true,
    +			'~' => true
    +		);
    +
    +		// $tokenTypes : maps keywords and operators to their corresponding token type
    +		$tokenTypes = array(
    +			'!'          => self::TYPE_UN_OP,
    +			'~'          => self::TYPE_UN_OP,
    +			'delete'     => self::TYPE_UN_OP,
    +			'new'        => self::TYPE_UN_OP,
    +			'typeof'     => self::TYPE_UN_OP,
    +			'void'       => self::TYPE_UN_OP,
    +			'++'         => self::TYPE_INCR_OP,
    +			'--'         => self::TYPE_INCR_OP,
    +			'!='         => self::TYPE_BIN_OP,
    +			'!=='        => self::TYPE_BIN_OP,
    +			'%'          => self::TYPE_BIN_OP,
    +			'%='         => self::TYPE_BIN_OP,
    +			'&'          => self::TYPE_BIN_OP,
    +			'&&'         => self::TYPE_BIN_OP,
    +			'&='         => self::TYPE_BIN_OP,
    +			'*'          => self::TYPE_BIN_OP,
    +			'*='         => self::TYPE_BIN_OP,
    +			'+='         => self::TYPE_BIN_OP,
    +			'-='         => self::TYPE_BIN_OP,
    +			'.'          => self::TYPE_BIN_OP,
    +			'/'          => self::TYPE_BIN_OP,
    +			'/='         => self::TYPE_BIN_OP,
    +			'<'          => self::TYPE_BIN_OP,
    +			'<<'         => self::TYPE_BIN_OP,
    +			'<<='        => self::TYPE_BIN_OP,
    +			'<='         => self::TYPE_BIN_OP,
    +			'='          => self::TYPE_BIN_OP,
    +			'=='         => self::TYPE_BIN_OP,
    +			'==='        => self::TYPE_BIN_OP,
    +			'>'          => self::TYPE_BIN_OP,
    +			'>='         => self::TYPE_BIN_OP,
    +			'>>'         => self::TYPE_BIN_OP,
    +			'>>='        => self::TYPE_BIN_OP,
    +			'>>>'        => self::TYPE_BIN_OP,
    +			'>>>='       => self::TYPE_BIN_OP,
    +			'^'          => self::TYPE_BIN_OP,
    +			'^='         => self::TYPE_BIN_OP,
    +			'|'          => self::TYPE_BIN_OP,
    +			'|='         => self::TYPE_BIN_OP,
    +			'||'         => self::TYPE_BIN_OP,
    +			'in'         => self::TYPE_BIN_OP,
    +			'instanceof' => self::TYPE_BIN_OP,
    +			'+'          => self::TYPE_ADD_OP,
    +			'-'          => self::TYPE_ADD_OP,
    +			'?'          => self::TYPE_HOOK,
    +			':'          => self::TYPE_COLON,
    +			','          => self::TYPE_COMMA,
    +			';'          => self::TYPE_SEMICOLON,
    +			'{'          => self::TYPE_BRACE_OPEN,
    +			'}'          => self::TYPE_BRACE_CLOSE,
    +			'('          => self::TYPE_PAREN_OPEN,
    +			'['          => self::TYPE_PAREN_OPEN,
    +			')'          => self::TYPE_PAREN_CLOSE,
    +			']'          => self::TYPE_PAREN_CLOSE,
    +			'break'      => self::TYPE_RETURN,
    +			'continue'   => self::TYPE_RETURN,
    +			'return'     => self::TYPE_RETURN,
    +			'throw'      => self::TYPE_RETURN,
    +			'catch'      => self::TYPE_IF,
    +			'for'        => self::TYPE_IF,
    +			'if'         => self::TYPE_IF,
    +			'switch'     => self::TYPE_IF,
    +			'while'      => self::TYPE_IF,
    +			'with'       => self::TYPE_IF,
    +			'case'       => self::TYPE_DO,
    +			'do'         => self::TYPE_DO,
    +			'else'       => self::TYPE_DO,
    +			'finally'    => self::TYPE_DO,
    +			'try'        => self::TYPE_DO,
    +			'var'        => self::TYPE_DO,
    +			'function'   => self::TYPE_FUNC
    +		);
    +
    +		// $goto : This is the main table for our state machine. For every state/token pair
    +		//         the following state is defined. When no rule exists for a given pair,
    +		//         the state is left unchanged.
    +		$goto = array(
    +			self::STATEMENT => array(
    +				self::TYPE_UN_OP      => self::EXPRESSION,
    +				self::TYPE_INCR_OP    => self::EXPRESSION,
    +				self::TYPE_ADD_OP     => self::EXPRESSION,
    +				self::TYPE_PAREN_OPEN => self::PAREN_EXPRESSION,
    +				self::TYPE_RETURN     => self::EXPRESSION_NO_NL,
    +				self::TYPE_IF         => self::CONDITION,
    +				self::TYPE_FUNC       => self::CONDITION,
    +				self::TYPE_LITERAL    => self::EXPRESSION_OP
    +			),
    +			self::CONDITION => array(
    +				self::TYPE_PAREN_OPEN => self::PAREN_EXPRESSION
    +			),
    +			self::PROPERTY_ASSIGNMENT => array(
    +				self::TYPE_COLON      => self::PROPERTY_EXPRESSION,
    +				self::TYPE_BRACE_OPEN => self::STATEMENT
    +			),
    +			self::EXPRESSION => array(
    +				self::TYPE_SEMICOLON  => self::STATEMENT,
    +				self::TYPE_BRACE_OPEN => self::PROPERTY_ASSIGNMENT,
    +				self::TYPE_PAREN_OPEN => self::PAREN_EXPRESSION,
    +				self::TYPE_FUNC       => self::EXPRESSION_FUNC,
    +				self::TYPE_LITERAL    => self::EXPRESSION_OP
    +			),
    +			self::EXPRESSION_NO_NL => array(
    +				self::TYPE_SEMICOLON  => self::STATEMENT,
    +				self::TYPE_BRACE_OPEN => self::PROPERTY_ASSIGNMENT,
    +				self::TYPE_PAREN_OPEN => self::PAREN_EXPRESSION,
    +				self::TYPE_FUNC       => self::EXPRESSION_FUNC,
    +				self::TYPE_LITERAL    => self::EXPRESSION_OP
    +			),
    +			self::EXPRESSION_OP => array(
    +				self::TYPE_BIN_OP     => self::EXPRESSION,
    +				self::TYPE_ADD_OP     => self::EXPRESSION,
    +				self::TYPE_HOOK       => self::EXPRESSION_TERNARY,
    +				self::TYPE_COLON      => self::STATEMENT,
    +				self::TYPE_COMMA      => self::EXPRESSION,
    +				self::TYPE_SEMICOLON  => self::STATEMENT,
    +				self::TYPE_PAREN_OPEN => self::PAREN_EXPRESSION
    +			),
    +			self::EXPRESSION_FUNC => array(
    +				self::TYPE_BRACE_OPEN => self::STATEMENT
    +			),
    +			self::EXPRESSION_TERNARY => array(
    +				self::TYPE_BRACE_OPEN => self::PROPERTY_ASSIGNMENT,
    +				self::TYPE_PAREN_OPEN => self::PAREN_EXPRESSION,
    +				self::TYPE_FUNC       => self::EXPRESSION_TERNARY_FUNC,
    +				self::TYPE_LITERAL    => self::EXPRESSION_TERNARY_OP
    +			),
    +			self::EXPRESSION_TERNARY_OP => array(
    +				self::TYPE_BIN_OP     => self::EXPRESSION_TERNARY,
    +				self::TYPE_ADD_OP     => self::EXPRESSION_TERNARY,
    +				self::TYPE_HOOK       => self::EXPRESSION_TERNARY,
    +				self::TYPE_COMMA      => self::EXPRESSION_TERNARY,
    +				self::TYPE_PAREN_OPEN => self::PAREN_EXPRESSION
    +			),
    +			self::EXPRESSION_TERNARY_FUNC => array(
    +				self::TYPE_BRACE_OPEN => self::STATEMENT
    +			),
    +			self::PAREN_EXPRESSION => array(
    +				self::TYPE_BRACE_OPEN => self::PROPERTY_ASSIGNMENT,
    +				self::TYPE_PAREN_OPEN => self::PAREN_EXPRESSION,
    +				self::TYPE_FUNC       => self::PAREN_EXPRESSION_FUNC,
    +				self::TYPE_LITERAL    => self::PAREN_EXPRESSION_OP
    +			),
    +			self::PAREN_EXPRESSION_OP => array(
    +				self::TYPE_BIN_OP     => self::PAREN_EXPRESSION,
    +				self::TYPE_ADD_OP     => self::PAREN_EXPRESSION,
    +				self::TYPE_HOOK       => self::PAREN_EXPRESSION,
    +				self::TYPE_COLON      => self::PAREN_EXPRESSION,
    +				self::TYPE_COMMA      => self::PAREN_EXPRESSION,
    +				self::TYPE_SEMICOLON  => self::PAREN_EXPRESSION,
    +				self::TYPE_PAREN_OPEN => self::PAREN_EXPRESSION
    +			),
    +			self::PAREN_EXPRESSION_FUNC => array(
    +				self::TYPE_BRACE_OPEN => self::STATEMENT
    +			),
    +			self::PROPERTY_EXPRESSION => array(
    +				self::TYPE_BRACE_OPEN => self::PROPERTY_ASSIGNMENT,
    +				self::TYPE_PAREN_OPEN => self::PAREN_EXPRESSION,
    +				self::TYPE_FUNC       => self::PROPERTY_EXPRESSION_FUNC,
    +				self::TYPE_LITERAL    => self::PROPERTY_EXPRESSION_OP
    +			),
    +			self::PROPERTY_EXPRESSION_OP => array(
    +				self::TYPE_BIN_OP     => self::PROPERTY_EXPRESSION,
    +				self::TYPE_ADD_OP     => self::PROPERTY_EXPRESSION,
    +				self::TYPE_HOOK       => self::PROPERTY_EXPRESSION,
    +				self::TYPE_COMMA      => self::PROPERTY_ASSIGNMENT,
    +				self::TYPE_PAREN_OPEN => self::PAREN_EXPRESSION
    +			),
    +			self::PROPERTY_EXPRESSION_FUNC => array(
    +				self::TYPE_BRACE_OPEN => self::STATEMENT
    +			)
    +		);
    +
    +		// $push : This table contains the rules for when to push a state onto the stack.
    +		//         The pushed state is the state to return to when the corresponding
    +		//         closing token is found
    +		$push = array(
    +			self::STATEMENT => array(
    +				self::TYPE_BRACE_OPEN => self::STATEMENT,
    +				self::TYPE_PAREN_OPEN => self::EXPRESSION_OP
    +			),
    +			self::CONDITION => array(
    +				self::TYPE_PAREN_OPEN => self::STATEMENT
    +			),
    +			self::PROPERTY_ASSIGNMENT => array(
    +				self::TYPE_BRACE_OPEN => self::PROPERTY_ASSIGNMENT
    +			),
    +			self::EXPRESSION => array(
    +				self::TYPE_BRACE_OPEN => self::EXPRESSION_OP,
    +				self::TYPE_PAREN_OPEN => self::EXPRESSION_OP
    +			),
    +			self::EXPRESSION_NO_NL => array(
    +				self::TYPE_BRACE_OPEN => self::EXPRESSION_OP,
    +				self::TYPE_PAREN_OPEN => self::EXPRESSION_OP
    +			),
    +			self::EXPRESSION_OP => array(
    +				self::TYPE_HOOK       => self::EXPRESSION,
    +				self::TYPE_PAREN_OPEN => self::EXPRESSION_OP
    +			),
    +			self::EXPRESSION_FUNC => array(
    +				self::TYPE_BRACE_OPEN => self::EXPRESSION_OP
    +			),
    +			self::EXPRESSION_TERNARY => array(
    +				self::TYPE_BRACE_OPEN => self::EXPRESSION_TERNARY_OP,
    +				self::TYPE_PAREN_OPEN => self::EXPRESSION_TERNARY_OP
    +			),
    +			self::EXPRESSION_TERNARY_OP => array(
    +				self::TYPE_HOOK       => self::EXPRESSION_TERNARY,
    +				self::TYPE_PAREN_OPEN => self::EXPRESSION_TERNARY_OP
    +			),
    +			self::EXPRESSION_TERNARY_FUNC => array(
    +				self::TYPE_BRACE_OPEN => self::EXPRESSION_TERNARY_OP
    +			),
    +			self::PAREN_EXPRESSION => array(
    +				self::TYPE_BRACE_OPEN => self::PAREN_EXPRESSION_OP,
    +				self::TYPE_PAREN_OPEN => self::PAREN_EXPRESSION_OP
    +			),
    +			self::PAREN_EXPRESSION_OP => array(
    +				self::TYPE_PAREN_OPEN => self::PAREN_EXPRESSION_OP
    +			),
    +			self::PAREN_EXPRESSION_FUNC => array(
    +				self::TYPE_BRACE_OPEN => self::PAREN_EXPRESSION_OP
    +			),
    +			self::PROPERTY_EXPRESSION => array(
    +				self::TYPE_BRACE_OPEN => self::PROPERTY_EXPRESSION_OP,
    +				self::TYPE_PAREN_OPEN => self::PROPERTY_EXPRESSION_OP
    +			),
    +			self::PROPERTY_EXPRESSION_OP => array(
    +				self::TYPE_PAREN_OPEN => self::PROPERTY_EXPRESSION_OP
    +			),
    +			self::PROPERTY_EXPRESSION_FUNC => array(
    +				self::TYPE_BRACE_OPEN => self::PROPERTY_EXPRESSION_OP
    +			)
    +		);
    +
    +		// $pop : Rules for when to pop a state from the stack
    +		$pop = array(
    +			self::STATEMENT              => array( self::TYPE_BRACE_CLOSE => true ),
    +			self::PROPERTY_ASSIGNMENT    => array( self::TYPE_BRACE_CLOSE => true ),
    +			self::EXPRESSION             => array( self::TYPE_BRACE_CLOSE => true ),
    +			self::EXPRESSION_NO_NL       => array( self::TYPE_BRACE_CLOSE => true ),
    +			self::EXPRESSION_OP          => array( self::TYPE_BRACE_CLOSE => true ),
    +			self::EXPRESSION_TERNARY_OP  => array( self::TYPE_COLON       => true ),
    +			self::PAREN_EXPRESSION       => array( self::TYPE_PAREN_CLOSE => true ),
    +			self::PAREN_EXPRESSION_OP    => array( self::TYPE_PAREN_CLOSE => true ),
    +			self::PROPERTY_EXPRESSION    => array( self::TYPE_BRACE_CLOSE => true ),
    +			self::PROPERTY_EXPRESSION_OP => array( self::TYPE_BRACE_CLOSE => true )
    +		);
    +
    +		// $semicolon : Rules for when a semicolon insertion is appropriate
    +		$semicolon = array(
    +			self::EXPRESSION_NO_NL => array(
    +				self::TYPE_UN_OP      => true,
    +				self::TYPE_INCR_OP    => true,
    +				self::TYPE_ADD_OP     => true,
    +				self::TYPE_BRACE_OPEN => true,
    +				self::TYPE_PAREN_OPEN => true,
    +				self::TYPE_RETURN     => true,
    +				self::TYPE_IF         => true,
    +				self::TYPE_DO         => true,
    +				self::TYPE_FUNC       => true,
    +				self::TYPE_LITERAL    => true
    +			),
    +			self::EXPRESSION_OP => array(
    +				self::TYPE_UN_OP      => true,
    +				self::TYPE_INCR_OP    => true,
    +				self::TYPE_BRACE_OPEN => true,
    +				self::TYPE_RETURN     => true,
    +				self::TYPE_IF         => true,
    +				self::TYPE_DO         => true,
    +				self::TYPE_FUNC       => true,
    +				self::TYPE_LITERAL    => true
    +			)
    +		);
    +
    +		// Rules for when newlines should be inserted if
    +		// $statementsOnOwnLine is enabled.
    +		// $newlineBefore is checked before switching state,
    +		// $newlineAfter is checked after
    +		$newlineBefore = array(
    +			self::STATEMENT => array(
    +				self::TYPE_BRACE_CLOSE => true,
    +			),
    +		);
    +		$newlineAfter = array(
    +			self::STATEMENT => array(
    +				self::TYPE_BRACE_OPEN => true,
    +				self::TYPE_PAREN_CLOSE => true,
    +				self::TYPE_SEMICOLON => true,
    +			),
    +		);
    +
    +		// $divStates : Contains all states that can be followed by a division operator
    +		$divStates = array(
    +			self::EXPRESSION_OP          => true,
    +			self::EXPRESSION_TERNARY_OP  => true,
    +			self::PAREN_EXPRESSION_OP    => true,
    +			self::PROPERTY_EXPRESSION_OP => true
    +		);
    +
    +		// Here's where the minifying takes place: Loop through the input, looking for tokens
    +		// and output them to $out, taking actions to the above defined rules when appropriate.
    +		$out = '';
    +		$pos = 0;
    +		$length = strlen( $s );
    +		$lineLength = 0;
    +		$newlineFound = true;
    +		$state = self::STATEMENT;
    +		$stack = array();
    +		$last = ';'; // Pretend that we have seen a semicolon yet
    +		while( $pos < $length ) {
    +			// First, skip over any whitespace and multiline comments, recording whether we
    +			// found any newline character
    +			$skip = strspn( $s, " \t\n\r\xb\xc", $pos );
    +			if( !$skip ) {
    +				$ch = $s[$pos];
    +				if( $ch === '/' && substr( $s, $pos, 2 ) === '/*' ) {
    +					// Multiline comment. Search for the end token or EOT.
    +					$end = strpos( $s, '*/', $pos + 2 );
    +					$skip = $end === false ? $length - $pos : $end - $pos + 2;
    +				}
    +			}
    +			if( $skip ) {
    +				// The semicolon insertion mechanism needs to know whether there was a newline
    +				// between two tokens, so record it now.
    +				if( !$newlineFound && strcspn( $s, "\r\n", $pos, $skip ) !== $skip ) {
    +					$newlineFound = true;
    +				}
    +				$pos += $skip;
    +				continue;
    +			}
    +			// Handle C++-style comments and html comments, which are treated as single line
    +			// comments by the browser, regardless of whether the end tag is on the same line.
    +			// Handle --> the same way, but only if it's at the beginning of the line
    +			if( ( $ch === '/' && substr( $s, $pos, 2 ) === '//' )
    +				|| ( $ch === '<' && substr( $s, $pos, 4 ) === '' )
    +			) {
    +				$pos += strcspn( $s, "\r\n", $pos );
    +				continue;
    +			}
    +
    +			// Find out which kind of token we're handling. $end will point past the end of it.
    +			$end = $pos + 1;
    +			// Handle string literals
    +			if( $ch === "'" || $ch === '"' ) {
    +				// Search to the end of the string literal, skipping over backslash escapes
    +				$search = $ch . '\\';
    +				do{
    +					$end += strcspn( $s, $search, $end ) + 2;
    +				} while( $end - 2 < $length && $s[$end - 2] === '\\' );
    +				$end--;
    +			// We have to distinguish between regexp literals and division operators
    +			// A division operator is only possible in certain states
    +			} elseif( $ch === '/' && !isset( $divStates[$state] ) ) {
    +				// Regexp literal, search to the end, skipping over backslash escapes and
    +				// character classes
    +				for( ; ; ) {
    +					do{
    +						$end += strcspn( $s, '/[\\', $end ) + 2;
    +					} while( $end - 2 < $length && $s[$end - 2] === '\\' );
    +					$end--;
    +					if( $end - 1 >= $length || $s[$end - 1] === '/' ) {
    +						break;
    +					}
    +					do{
    +						$end += strcspn( $s, ']\\', $end ) + 2;
    +					} while( $end - 2 < $length && $s[$end - 2] === '\\' );
    +					$end--;
    +				};
    +				// Search past the regexp modifiers (gi)
    +				while( $end < $length && ctype_alpha( $s[$end] ) ) {
    +					$end++;
    +				}
    +			} elseif(
    +				$ch === '0'
    +				&& ($pos + 1 < $length) && ($s[$pos + 1] === 'x' || $s[$pos + 1] === 'X' )
    +			) {
    +				// Hex numeric literal
    +				$end++; // x or X
    +				$len = strspn( $s, '0123456789ABCDEFabcdef', $end );
    +				if ( !$len ) {
    +					return self::parseError($s, $pos, 'Expected a hexadecimal number but found ' . substr( $s, $pos, 5 ) . '...' );
    +				}
    +				$end += $len;
    +			} elseif(
    +				ctype_digit( $ch )
    +				|| ( $ch === '.' && $pos + 1 < $length && ctype_digit( $s[$pos + 1] ) )
    +			) {
    +				$end += strspn( $s, '0123456789', $end );
    +				$decimal = strspn( $s, '.', $end );
    +				if ($decimal) {
    +					if ( $decimal > 2 ) {
    +						return self::parseError($s, $end, 'The number has too many decimal points' );
    +					}
    +					$end += strspn( $s, '0123456789', $end + 1 ) + $decimal;
    +				}
    +				$exponent = strspn( $s, 'eE', $end );
    +				if( $exponent ) {
    +					if ( $exponent > 1 ) {
    +						return self::parseError($s, $end, 'Number with several E' );
    +					}
    +					$end++;
    +
    +					// + sign is optional; - sign is required.
    +					$end += strspn( $s, '-+', $end );
    +					$len = strspn( $s, '0123456789', $end );
    +					if ( !$len ) {
    +						return self::parseError($s, $pos, 'No decimal digits after e, how many zeroes should be added?' );
    +					}
    +					$end += $len;
    +				}
    +			} elseif( isset( $opChars[$ch] ) ) {
    +				// Punctuation character. Search for the longest matching operator.
    +				while(
    +					$end < $length
    +					&& isset( $tokenTypes[substr( $s, $pos, $end - $pos + 1 )] )
    +				) {
    +					$end++;
    +				}
    +			} else {
    +				// Identifier or reserved word. Search for the end by excluding whitespace and
    +				// punctuation.
    +				$end += strcspn( $s, " \t\n.;,=<>+-{}()[]?:*/%'\"!&|^~\xb\xc\r", $end );
    +			}
    +
    +			// Now get the token type from our type array
    +			$token = substr( $s, $pos, $end - $pos ); // so $end - $pos == strlen( $token )
    +			$type = isset( $tokenTypes[$token] ) ? $tokenTypes[$token] : self::TYPE_LITERAL;
    +
    +			if( $newlineFound && isset( $semicolon[$state][$type] ) ) {
    +				// This token triggers the semicolon insertion mechanism of javascript. While we
    +				// could add the ; token here ourselves, keeping the newline has a few advantages.
    +				$out .= "\n";
    +				$state = self::STATEMENT;
    +				$lineLength = 0;
    +			} elseif( $maxLineLength > 0 && $lineLength + $end - $pos > $maxLineLength &&
    +					!isset( $semicolon[$state][$type] ) && $type !== self::TYPE_INCR_OP )
    +			{
    +				// This line would get too long if we added $token, so add a newline first.
    +				// Only do this if it won't trigger semicolon insertion and if it won't
    +				// put a postfix increment operator on its own line, which is illegal in js.
    +				$out .= "\n";
    +				$lineLength = 0;
    +			// Check, whether we have to separate the token from the last one with whitespace
    +			} elseif( !isset( $opChars[$last] ) && !isset( $opChars[$ch] ) ) {
    +				$out .= ' ';
    +				$lineLength++;
    +			// Don't accidentally create ++, -- or // tokens
    +			} elseif( $last === $ch && ( $ch === '+' || $ch === '-' || $ch === '/' ) ) {
    +				$out .= ' ';
    +				$lineLength++;
    +			}
    +
    +			$out .= $token;
    +			$lineLength += $end - $pos; // += strlen( $token )
    +			$last = $s[$end - 1];
    +			$pos = $end;
    +			$newlineFound = false;
    +
    +			// Output a newline after the token if required
    +			// This is checked before AND after switching state
    +			$newlineAdded = false;
    +			if ( $statementsOnOwnLine && !$newlineAdded && isset( $newlineBefore[$state][$type] ) ) {
    +				$out .= "\n";
    +				$lineLength = 0;
    +				$newlineAdded = true;
    +			}
    +
    +			// Now that we have output our token, transition into the new state.
    +			if( isset( $push[$state][$type] ) && count( $stack ) < self::STACK_LIMIT ) {
    +				$stack[] = $push[$state][$type];
    +			}
    +			if( $stack && isset( $pop[$state][$type] ) ) {
    +				$state = array_pop( $stack );
    +			} elseif( isset( $goto[$state][$type] ) ) {
    +				$state = $goto[$state][$type];
    +			}
    +
    +			// Check for newline insertion again
    +			if ( $statementsOnOwnLine && !$newlineAdded && isset( $newlineAfter[$state][$type] ) ) {
    +				$out .= "\n";
    +				$lineLength = 0;
    +			}
    +		}
    +		return $out;
    +	}
    +
    +	static function parseError($fullJavascript, $position, $errorMsg) {
    +		// TODO: Handle the error: trigger_error, throw exception, return false...
    +		return false;
    +	}
    +}
    diff --git a/3rdparty/miniColors/GPL-LICENSE.txt b/3rdparty/miniColors/GPL-LICENSE.txt
    new file mode 100644
    index 0000000000..11dddd00ef
    --- /dev/null
    +++ b/3rdparty/miniColors/GPL-LICENSE.txt
    @@ -0,0 +1,278 @@
    +        GNU GENERAL PUBLIC LICENSE
    +           Version 2, June 1991
    +
    + Copyright (C) 1989, 1991 Free Software Foundation, Inc.
    + 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
    + Everyone is permitted to copy and distribute verbatim copies
    + of this license document, but changing it is not allowed.
    +
    +          Preamble
    +
    +  The licenses for most software are designed to take away your
    +freedom to share and change it.  By contrast, the GNU General Public
    +License is intended to guarantee your freedom to share and change free
    +software--to make sure the software is free for all its users.  This
    +General Public License applies to most of the Free Software
    +Foundation's software and to any other program whose authors commit to
    +using it.  (Some other Free Software Foundation software is covered by
    +the GNU Lesser General Public License instead.)  You can apply it to
    +your programs, too.
    +
    +  When we speak of free software, we are referring to freedom, not
    +price.  Our General Public Licenses are designed to make sure that you
    +have the freedom to distribute copies of free software (and charge for
    +this service if you wish), that you receive source code or can get it
    +if you want it, that you can change the software or use pieces of it
    +in new free programs; and that you know you can do these things.
    +
    +  To protect your rights, we need to make restrictions that forbid
    +anyone to deny you these rights or to ask you to surrender the rights.
    +These restrictions translate to certain responsibilities for you if you
    +distribute copies of the software, or if you modify it.
    +
    +  For example, if you distribute copies of such a program, whether
    +gratis or for a fee, you must give the recipients all the rights that
    +you have.  You must make sure that they, too, receive or can get the
    +source code.  And you must show them these terms so they know their
    +rights.
    +
    +  We protect your rights with two steps: (1) copyright the software, and
    +(2) offer you this license which gives you legal permission to copy,
    +distribute and/or modify the software.
    +
    +  Also, for each author's protection and ours, we want to make certain
    +that everyone understands that there is no warranty for this free
    +software.  If the software is modified by someone else and passed on, we
    +want its recipients to know that what they have is not the original, so
    +that any problems introduced by others will not reflect on the original
    +authors' reputations.
    +
    +  Finally, any free program is threatened constantly by software
    +patents.  We wish to avoid the danger that redistributors of a free
    +program will individually obtain patent licenses, in effect making the
    +program proprietary.  To prevent this, we have made it clear that any
    +patent must be licensed for everyone's free use or not licensed at all.
    +
    +  The precise terms and conditions for copying, distribution and
    +modification follow.
    +
    +        GNU GENERAL PUBLIC LICENSE
    +   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
    +
    +  0. This License applies to any program or other work which contains
    +a notice placed by the copyright holder saying it may be distributed
    +under the terms of this General Public License.  The "Program", below,
    +refers to any such program or work, and a "work based on the Program"
    +means either the Program or any derivative work under copyright law:
    +that is to say, a work containing the Program or a portion of it,
    +either verbatim or with modifications and/or translated into another
    +language.  (Hereinafter, translation is included without limitation in
    +the term "modification".)  Each licensee is addressed as "you".
    +
    +Activities other than copying, distribution and modification are not
    +covered by this License; they are outside its scope.  The act of
    +running the Program is not restricted, and the output from the Program
    +is covered only if its contents constitute a work based on the
    +Program (independent of having been made by running the Program).
    +Whether that is true depends on what the Program does.
    +
    +  1. You may copy and distribute verbatim copies of the Program's
    +source code as you receive it, in any medium, provided that you
    +conspicuously and appropriately publish on each copy an appropriate
    +copyright notice and disclaimer of warranty; keep intact all the
    +notices that refer to this License and to the absence of any warranty;
    +and give any other recipients of the Program a copy of this License
    +along with the Program.
    +
    +You may charge a fee for the physical act of transferring a copy, and
    +you may at your option offer warranty protection in exchange for a fee.
    +
    +  2. You may modify your copy or copies of the Program or any portion
    +of it, thus forming a work based on the Program, and copy and
    +distribute such modifications or work under the terms of Section 1
    +above, provided that you also meet all of these conditions:
    +
    +    a) You must cause the modified files to carry prominent notices
    +    stating that you changed the files and the date of any change.
    +
    +    b) You must cause any work that you distribute or publish, that in
    +    whole or in part contains or is derived from the Program or any
    +    part thereof, to be licensed as a whole at no charge to all third
    +    parties under the terms of this License.
    +
    +    c) If the modified program normally reads commands interactively
    +    when run, you must cause it, when started running for such
    +    interactive use in the most ordinary way, to print or display an
    +    announcement including an appropriate copyright notice and a
    +    notice that there is no warranty (or else, saying that you provide
    +    a warranty) and that users may redistribute the program under
    +    these conditions, and telling the user how to view a copy of this
    +    License.  (Exception: if the Program itself is interactive but
    +    does not normally print such an announcement, your work based on
    +    the Program is not required to print an announcement.)
    +
    +These requirements apply to the modified work as a whole.  If
    +identifiable sections of that work are not derived from the Program,
    +and can be reasonably considered independent and separate works in
    +themselves, then this License, and its terms, do not apply to those
    +sections when you distribute them as separate works.  But when you
    +distribute the same sections as part of a whole which is a work based
    +on the Program, the distribution of the whole must be on the terms of
    +this License, whose permissions for other licensees extend to the
    +entire whole, and thus to each and every part regardless of who wrote it.
    +
    +Thus, it is not the intent of this section to claim rights or contest
    +your rights to work written entirely by you; rather, the intent is to
    +exercise the right to control the distribution of derivative or
    +collective works based on the Program.
    +
    +In addition, mere aggregation of another work not based on the Program
    +with the Program (or with a work based on the Program) on a volume of
    +a storage or distribution medium does not bring the other work under
    +the scope of this License.
    +
    +  3. You may copy and distribute the Program (or a work based on it,
    +under Section 2) in object code or executable form under the terms of
    +Sections 1 and 2 above provided that you also do one of the following:
    +
    +    a) Accompany it with the complete corresponding machine-readable
    +    source code, which must be distributed under the terms of Sections
    +    1 and 2 above on a medium customarily used for software interchange; or,
    +
    +    b) Accompany it with a written offer, valid for at least three
    +    years, to give any third party, for a charge no more than your
    +    cost of physically performing source distribution, a complete
    +    machine-readable copy of the corresponding source code, to be
    +    distributed under the terms of Sections 1 and 2 above on a medium
    +    customarily used for software interchange; or,
    +
    +    c) Accompany it with the information you received as to the offer
    +    to distribute corresponding source code.  (This alternative is
    +    allowed only for noncommercial distribution and only if you
    +    received the program in object code or executable form with such
    +    an offer, in accord with Subsection b above.)
    +
    +The source code for a work means the preferred form of the work for
    +making modifications to it.  For an executable work, complete source
    +code means all the source code for all modules it contains, plus any
    +associated interface definition files, plus the scripts used to
    +control compilation and installation of the executable.  However, as a
    +special exception, the source code distributed need not include
    +anything that is normally distributed (in either source or binary
    +form) with the major components (compiler, kernel, and so on) of the
    +operating system on which the executable runs, unless that component
    +itself accompanies the executable.
    +
    +If distribution of executable or object code is made by offering
    +access to copy from a designated place, then offering equivalent
    +access to copy the source code from the same place counts as
    +distribution of the source code, even though third parties are not
    +compelled to copy the source along with the object code.
    +
    +  4. You may not copy, modify, sublicense, or distribute the Program
    +except as expressly provided under this License.  Any attempt
    +otherwise to copy, modify, sublicense or distribute the Program is
    +void, and will automatically terminate your rights under this License.
    +However, parties who have received copies, or rights, from you under
    +this License will not have their licenses terminated so long as such
    +parties remain in full compliance.
    +
    +  5. You are not required to accept this License, since you have not
    +signed it.  However, nothing else grants you permission to modify or
    +distribute the Program or its derivative works.  These actions are
    +prohibited by law if you do not accept this License.  Therefore, by
    +modifying or distributing the Program (or any work based on the
    +Program), you indicate your acceptance of this License to do so, and
    +all its terms and conditions for copying, distributing or modifying
    +the Program or works based on it.
    +
    +  6. Each time you redistribute the Program (or any work based on the
    +Program), the recipient automatically receives a license from the
    +original licensor to copy, distribute or modify the Program subject to
    +these terms and conditions.  You may not impose any further
    +restrictions on the recipients' exercise of the rights granted herein.
    +You are not responsible for enforcing compliance by third parties to
    +this License.
    +
    +  7. If, as a consequence of a court judgment or allegation of patent
    +infringement or for any other reason (not limited to patent issues),
    +conditions are imposed on you (whether by court order, agreement or
    +otherwise) that contradict the conditions of this License, they do not
    +excuse you from the conditions of this License.  If you cannot
    +distribute so as to satisfy simultaneously your obligations under this
    +License and any other pertinent obligations, then as a consequence you
    +may not distribute the Program at all.  For example, if a patent
    +license would not permit royalty-free redistribution of the Program by
    +all those who receive copies directly or indirectly through you, then
    +the only way you could satisfy both it and this License would be to
    +refrain entirely from distribution of the Program.
    +
    +If any portion of this section is held invalid or unenforceable under
    +any particular circumstance, the balance of the section is intended to
    +apply and the section as a whole is intended to apply in other
    +circumstances.
    +
    +It is not the purpose of this section to induce you to infringe any
    +patents or other property right claims or to contest validity of any
    +such claims; this section has the sole purpose of protecting the
    +integrity of the free software distribution system, which is
    +implemented by public license practices.  Many people have made
    +generous contributions to the wide range of software distributed
    +through that system in reliance on consistent application of that
    +system; it is up to the author/donor to decide if he or she is willing
    +to distribute software through any other system and a licensee cannot
    +impose that choice.
    +
    +This section is intended to make thoroughly clear what is believed to
    +be a consequence of the rest of this License.
    +
    +  8. If the distribution and/or use of the Program is restricted in
    +certain countries either by patents or by copyrighted interfaces, the
    +original copyright holder who places the Program under this License
    +may add an explicit geographical distribution limitation excluding
    +those countries, so that distribution is permitted only in or among
    +countries not thus excluded.  In such case, this License incorporates
    +the limitation as if written in the body of this License.
    +
    +  9. The Free Software Foundation may publish revised and/or new versions
    +of the General Public License from time to time.  Such new versions will
    +be similar in spirit to the present version, but may differ in detail to
    +address new problems or concerns.
    +
    +Each version is given a distinguishing version number.  If the Program
    +specifies a version number of this License which applies to it and "any
    +later version", you have the option of following the terms and conditions
    +either of that version or of any later version published by the Free
    +Software Foundation.  If the Program does not specify a version number of
    +this License, you may choose any version ever published by the Free Software
    +Foundation.
    +
    +  10. If you wish to incorporate parts of the Program into other free
    +programs whose distribution conditions are different, write to the author
    +to ask for permission.  For software which is copyrighted by the Free
    +Software Foundation, write to the Free Software Foundation; we sometimes
    +make exceptions for this.  Our decision will be guided by the two goals
    +of preserving the free status of all derivatives of our free software and
    +of promoting the sharing and reuse of software generally.
    +
    +          NO WARRANTY
    +
    +  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
    +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
    +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
    +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
    +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
    +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
    +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
    +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
    +REPAIR OR CORRECTION.
    +
    +  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
    +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
    +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
    +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
    +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
    +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
    +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
    +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
    +POSSIBILITY OF SUCH DAMAGES.
    diff --git a/3rdparty/miniColors/MIT-LICENSE.txt b/3rdparty/miniColors/MIT-LICENSE.txt
    new file mode 100644
    index 0000000000..ec6f758157
    --- /dev/null
    +++ b/3rdparty/miniColors/MIT-LICENSE.txt
    @@ -0,0 +1,20 @@
    +Copyright (c) Cory LaViska
    +
    +Permission is hereby granted, free of charge, to any person obtaining
    +a copy of this software and associated documentation files (the
    +"Software"), to deal in the Software without restriction, including
    +without limitation the rights to use, copy, modify, merge, publish,
    +distribute, sublicense, and/or sell copies of the Software, and to
    +permit persons to whom the Software is furnished to do so, subject to
    +the following conditions:
    +
    +The above copyright notice and this permission notice shall be
    +included in all copies or substantial portions of the Software.
    +
    +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
    +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
    +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
    +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    diff --git a/3rdparty/miniColors/css/images/colors.png b/3rdparty/miniColors/css/images/colors.png
    new file mode 100755
    index 0000000000..1b4f819d8d
    Binary files /dev/null and b/3rdparty/miniColors/css/images/colors.png differ
    diff --git a/3rdparty/miniColors/css/images/trigger.png b/3rdparty/miniColors/css/images/trigger.png
    new file mode 100755
    index 0000000000..8c169fd605
    Binary files /dev/null and b/3rdparty/miniColors/css/images/trigger.png differ
    diff --git a/3rdparty/miniColors/css/jquery.miniColors.css b/3rdparty/miniColors/css/jquery.miniColors.css
    new file mode 100755
    index 0000000000..381bc1dc06
    --- /dev/null
    +++ b/3rdparty/miniColors/css/jquery.miniColors.css
    @@ -0,0 +1,81 @@
    +.miniColors-trigger {
    +	height: 22px;
    +	width: 22px;
    +	background: url(images/trigger.png) center no-repeat;
    +	vertical-align: middle;
    +	margin: 0 .25em;
    +	display: inline-block;
    +	outline: none;
    +}
    +
    +.miniColors-selector {
    +	position: absolute;
    +	width: 175px;
    +	height: 150px;
    +	background: #FFF;
    +	border: solid 1px #BBB;
    +	-moz-box-shadow: 0 0 6px rgba(0, 0, 0, .25);
    +	-webkit-box-shadow: 0 0 6px rgba(0, 0, 0, .25);
    +	box-shadow: 0 0 6px rgba(0, 0, 0, .25);
    +	-moz-border-radius: 5px;
    +	-webkit-border-radius: 5px;
    +	border-radius: 5px;
    +	padding: 5px;
    +	z-index: 999999;
    +}
    +
    +.miniColors-selector.black {
    +	background: #000;
    +	border-color: #000;
    +}
    +
    +.miniColors-colors {
    +	position: absolute;
    +	top: 5px;
    +	left: 5px;
    +	width: 150px;
    +	height: 150px;
    +	background: url(images/colors.png) right no-repeat;
    +	cursor: crosshair;
    +}
    +
    +.miniColors-hues {
    +	position: absolute;
    +	top: 5px;
    +	left: 160px;
    +	width: 20px;
    +	height: 150px;
    +	background: url(images/colors.png) left no-repeat;
    +	cursor: crosshair;
    +}
    +
    +.miniColors-colorPicker {
    +	position: absolute;
    +	width: 9px;
    +	height: 9px;
    +	border: 1px solid #fff;
    +	-moz-border-radius: 11px;
    +	-webkit-border-radius: 11px;
    +	border-radius: 11px;
    +}
    +.miniColors-colorPicker-inner {
    +	position: absolute;
    +	top: 0;
    +	left: 0; 
    +	width: 7px;
    +	height: 7px;
    +	border: 1px solid #000;
    +	-moz-border-radius: 9px;
    +	-webkit-border-radius: 9px;
    +	border-radius: 9px;
    +}
    +
    +.miniColors-huePicker {
    +	position: absolute;
    +	left: -3px;
    +	width: 24px;
    +	height: 1px;
    +	border: 1px solid #fff;
    +	border-radius: 2px;
    +	background: #000;
    +}
    \ No newline at end of file
    diff --git a/3rdparty/miniColors/js/jquery.miniColors.js b/3rdparty/miniColors/js/jquery.miniColors.js
    new file mode 100755
    index 0000000000..187db3fa84
    --- /dev/null
    +++ b/3rdparty/miniColors/js/jquery.miniColors.js
    @@ -0,0 +1,580 @@
    +/*
    + * jQuery miniColors: A small color selector
    + *
    + * Copyright 2011 Cory LaViska for A Beautiful Site, LLC. (http://abeautifulsite.net/)
    + *
    + * Dual licensed under the MIT or GPL Version 2 licenses
    + *
    +*/
    +if(jQuery) (function($) {
    +	
    +	$.extend($.fn, {
    +		
    +		miniColors: function(o, data) {
    +			
    +			var create = function(input, o, data) {
    +				//
    +				// Creates a new instance of the miniColors selector
    +				//
    +				
    +				// Determine initial color (defaults to white)
    +				var color = expandHex(input.val());
    +				if( !color ) color = 'ffffff';
    +				var hsb = hex2hsb(color);
    +				
    +				// Create trigger
    +				var trigger = $('');
    +				trigger.insertAfter(input);
    +				
    +				// Set input data and update attributes
    +				input
    +					.addClass('miniColors')
    +					.data('original-maxlength', input.attr('maxlength') || null)
    +					.data('original-autocomplete', input.attr('autocomplete') || null)
    +					.data('letterCase', 'uppercase')
    +					.data('trigger', trigger)
    +					.data('hsb', hsb)
    +					.data('change', o.change ? o.change : null)
    +					.data('close', o.close ? o.close : null)
    +					.data('open', o.open ? o.open : null)
    +					.attr('maxlength', 7)
    +					.attr('autocomplete', 'off')
    +					.val('#' + convertCase(color, o.letterCase));
    +				
    +				// Handle options
    +				if( o.readonly ) input.prop('readonly', true);
    +				if( o.disabled ) disable(input);
    +				
    +				// Show selector when trigger is clicked
    +				trigger.bind('click.miniColors', function(event) {
    +					event.preventDefault();
    +					if( input.val() === '' ) input.val('#');
    +					show(input);
    +
    +				});
    +				
    +				// Show selector when input receives focus
    +				input.bind('focus.miniColors', function(event) {
    +					if( input.val() === '' ) input.val('#');
    +					show(input);
    +				});
    +				
    +				// Hide on blur
    +				input.bind('blur.miniColors', function(event) {
    +					var hex = expandHex( hsb2hex(input.data('hsb')) );
    +					input.val( hex ? '#' + convertCase(hex, input.data('letterCase')) : '' );
    +				});
    +				
    +				// Hide when tabbing out of the input
    +				input.bind('keydown.miniColors', function(event) {
    +					if( event.keyCode === 9 ) hide(input);
    +				});
    +				
    +				// Update when color is typed in
    +				input.bind('keyup.miniColors', function(event) {
    +					setColorFromInput(input);
    +				});
    +				
    +				// Handle pasting
    +				input.bind('paste.miniColors', function(event) {
    +					// Short pause to wait for paste to complete
    +					setTimeout( function() {
    +						setColorFromInput(input);
    +					}, 5);
    +				});
    +				
    +			};
    +			
    +			var destroy = function(input) {
    +				//
    +				// Destroys an active instance of the miniColors selector
    +				//
    +				
    +				hide();
    +				input = $(input);
    +				
    +				// Restore to original state
    +				input.data('trigger').remove();
    +				input
    +					.attr('autocomplete', input.data('original-autocomplete'))
    +					.attr('maxlength', input.data('original-maxlength'))
    +					.removeData()
    +					.removeClass('miniColors')
    +					.unbind('.miniColors');
    +				$(document).unbind('.miniColors');
    +			};
    +			
    +			var enable = function(input) {
    +				//
    +				// Enables the input control and the selector
    +				//
    +				input
    +					.prop('disabled', false)
    +					.data('trigger')
    +					.css('opacity', 1);
    +			};
    +			
    +			var disable = function(input) {
    +				//
    +				// Disables the input control and the selector
    +				//
    +				hide(input);
    +				input
    +					.prop('disabled', true)
    +					.data('trigger')
    +					.css('opacity', 0.5);
    +			};
    +			
    +			var show = function(input) {
    +				//
    +				// Shows the miniColors selector
    +				//
    +				if( input.prop('disabled') ) return false;
    +				
    +				// Hide all other instances 
    +				hide();				
    +				
    +				// Generate the selector
    +				var selector = $('
    '); + selector + .append('
    ') + .append('
    ') + .css({ + top: input.is(':visible') ? input.offset().top + input.outerHeight() : input.data('trigger').offset().top + input.data('trigger').outerHeight(), + left: input.is(':visible') ? input.offset().left : input.data('trigger').offset().left, + display: 'none' + }) + .addClass( input.attr('class') ); + + // Set background for colors + var hsb = input.data('hsb'); + selector + .find('.miniColors-colors') + .css('backgroundColor', '#' + hsb2hex({ h: hsb.h, s: 100, b: 100 })); + + // Set colorPicker position + var colorPosition = input.data('colorPosition'); + if( !colorPosition ) colorPosition = getColorPositionFromHSB(hsb); + selector.find('.miniColors-colorPicker') + .css('top', colorPosition.y + 'px') + .css('left', colorPosition.x + 'px'); + + // Set huePicker position + var huePosition = input.data('huePosition'); + if( !huePosition ) huePosition = getHuePositionFromHSB(hsb); + selector.find('.miniColors-huePicker').css('top', huePosition.y + 'px'); + + // Set input data + input + .data('selector', selector) + .data('huePicker', selector.find('.miniColors-huePicker')) + .data('colorPicker', selector.find('.miniColors-colorPicker')) + .data('mousebutton', 0); + + $('BODY').append(selector); + selector.fadeIn(100); + + // Prevent text selection in IE + selector.bind('selectstart', function() { return false; }); + + $(document).bind('mousedown.miniColors touchstart.miniColors', function(event) { + + input.data('mousebutton', 1); + var testSubject = $(event.target).parents().andSelf(); + + if( testSubject.hasClass('miniColors-colors') ) { + event.preventDefault(); + input.data('moving', 'colors'); + moveColor(input, event); + } + + if( testSubject.hasClass('miniColors-hues') ) { + event.preventDefault(); + input.data('moving', 'hues'); + moveHue(input, event); + } + + if( testSubject.hasClass('miniColors-selector') ) { + event.preventDefault(); + return; + } + + if( testSubject.hasClass('miniColors') ) return; + + hide(input); + }); + + $(document) + .bind('mouseup.miniColors touchend.miniColors', function(event) { + event.preventDefault(); + input.data('mousebutton', 0).removeData('moving'); + }) + .bind('mousemove.miniColors touchmove.miniColors', function(event) { + event.preventDefault(); + if( input.data('mousebutton') === 1 ) { + if( input.data('moving') === 'colors' ) moveColor(input, event); + if( input.data('moving') === 'hues' ) moveHue(input, event); + } + }); + + // Fire open callback + if( input.data('open') ) { + input.data('open').call(input.get(0), '#' + hsb2hex(hsb), hsb2rgb(hsb)); + } + + }; + + var hide = function(input) { + + // + // Hides one or more miniColors selectors + // + + // Hide all other instances if input isn't specified + if( !input ) input = '.miniColors'; + + $(input).each( function() { + var selector = $(this).data('selector'); + $(this).removeData('selector'); + $(selector).fadeOut(100, function() { + // Fire close callback + if( input.data('close') ) { + var hsb = input.data('hsb'), hex = hsb2hex(hsb); + input.data('close').call(input.get(0), '#' + hex, hsb2rgb(hsb)); + } + $(this).remove(); + }); + }); + + $(document).unbind('.miniColors'); + + }; + + var moveColor = function(input, event) { + + var colorPicker = input.data('colorPicker'); + + colorPicker.hide(); + + var position = { + x: event.pageX, + y: event.pageY + }; + + // Touch support + if( event.originalEvent.changedTouches ) { + position.x = event.originalEvent.changedTouches[0].pageX; + position.y = event.originalEvent.changedTouches[0].pageY; + } + position.x = position.x - input.data('selector').find('.miniColors-colors').offset().left - 5; + position.y = position.y - input.data('selector').find('.miniColors-colors').offset().top - 5; + if( position.x <= -5 ) position.x = -5; + if( position.x >= 144 ) position.x = 144; + if( position.y <= -5 ) position.y = -5; + if( position.y >= 144 ) position.y = 144; + + input.data('colorPosition', position); + colorPicker.css('left', position.x).css('top', position.y).show(); + + // Calculate saturation + var s = Math.round((position.x + 5) * 0.67); + if( s < 0 ) s = 0; + if( s > 100 ) s = 100; + + // Calculate brightness + var b = 100 - Math.round((position.y + 5) * 0.67); + if( b < 0 ) b = 0; + if( b > 100 ) b = 100; + + // Update HSB values + var hsb = input.data('hsb'); + hsb.s = s; + hsb.b = b; + + // Set color + setColor(input, hsb, true); + }; + + var moveHue = function(input, event) { + + var huePicker = input.data('huePicker'); + + huePicker.hide(); + + var position = { + y: event.pageY + }; + + // Touch support + if( event.originalEvent.changedTouches ) { + position.y = event.originalEvent.changedTouches[0].pageY; + } + + position.y = position.y - input.data('selector').find('.miniColors-colors').offset().top - 1; + if( position.y <= -1 ) position.y = -1; + if( position.y >= 149 ) position.y = 149; + input.data('huePosition', position); + huePicker.css('top', position.y).show(); + + // Calculate hue + var h = Math.round((150 - position.y - 1) * 2.4); + if( h < 0 ) h = 0; + if( h > 360 ) h = 360; + + // Update HSB values + var hsb = input.data('hsb'); + hsb.h = h; + + // Set color + setColor(input, hsb, true); + + }; + + var setColor = function(input, hsb, updateInput) { + input.data('hsb', hsb); + var hex = hsb2hex(hsb); + if( updateInput ) input.val( '#' + convertCase(hex, input.data('letterCase')) ); + input.data('trigger').css('backgroundColor', '#' + hex); + if( input.data('selector') ) input.data('selector').find('.miniColors-colors').css('backgroundColor', '#' + hsb2hex({ h: hsb.h, s: 100, b: 100 })); + + // Fire change callback + if( input.data('change') ) { + if( hex === input.data('lastChange') ) return; + input.data('change').call(input.get(0), '#' + hex, hsb2rgb(hsb)); + input.data('lastChange', hex); + } + + }; + + var setColorFromInput = function(input) { + + input.val('#' + cleanHex(input.val())); + var hex = expandHex(input.val()); + if( !hex ) return false; + + // Get HSB equivalent + var hsb = hex2hsb(hex); + + // If color is the same, no change required + var currentHSB = input.data('hsb'); + if( hsb.h === currentHSB.h && hsb.s === currentHSB.s && hsb.b === currentHSB.b ) return true; + + // Set colorPicker position + var colorPosition = getColorPositionFromHSB(hsb); + var colorPicker = $(input.data('colorPicker')); + colorPicker.css('top', colorPosition.y + 'px').css('left', colorPosition.x + 'px'); + input.data('colorPosition', colorPosition); + + // Set huePosition position + var huePosition = getHuePositionFromHSB(hsb); + var huePicker = $(input.data('huePicker')); + huePicker.css('top', huePosition.y + 'px'); + input.data('huePosition', huePosition); + + setColor(input, hsb); + + return true; + + }; + + var convertCase = function(string, letterCase) { + if( letterCase === 'lowercase' ) return string.toLowerCase(); + if( letterCase === 'uppercase' ) return string.toUpperCase(); + return string; + }; + + var getColorPositionFromHSB = function(hsb) { + var x = Math.ceil(hsb.s / 0.67); + if( x < 0 ) x = 0; + if( x > 150 ) x = 150; + var y = 150 - Math.ceil(hsb.b / 0.67); + if( y < 0 ) y = 0; + if( y > 150 ) y = 150; + return { x: x - 5, y: y - 5 }; + }; + + var getHuePositionFromHSB = function(hsb) { + var y = 150 - (hsb.h / 2.4); + if( y < 0 ) h = 0; + if( y > 150 ) h = 150; + return { y: y - 1 }; + }; + + var cleanHex = function(hex) { + return hex.replace(/[^A-F0-9]/ig, ''); + }; + + var expandHex = function(hex) { + hex = cleanHex(hex); + if( !hex ) return null; + if( hex.length === 3 ) hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2]; + return hex.length === 6 ? hex : null; + }; + + var hsb2rgb = function(hsb) { + var rgb = {}; + var h = Math.round(hsb.h); + var s = Math.round(hsb.s*255/100); + var v = Math.round(hsb.b*255/100); + if(s === 0) { + rgb.r = rgb.g = rgb.b = v; + } else { + var t1 = v; + var t2 = (255 - s) * v / 255; + var t3 = (t1 - t2) * (h % 60) / 60; + if( h === 360 ) h = 0; + if( h < 60 ) { rgb.r = t1; rgb.b = t2; rgb.g = t2 + t3; } + else if( h < 120 ) {rgb.g = t1; rgb.b = t2; rgb.r = t1 - t3; } + else if( h < 180 ) {rgb.g = t1; rgb.r = t2; rgb.b = t2 + t3; } + else if( h < 240 ) {rgb.b = t1; rgb.r = t2; rgb.g = t1 - t3; } + else if( h < 300 ) {rgb.b = t1; rgb.g = t2; rgb.r = t2 + t3; } + else if( h < 360 ) {rgb.r = t1; rgb.g = t2; rgb.b = t1 - t3; } + else { rgb.r = 0; rgb.g = 0; rgb.b = 0; } + } + return { + r: Math.round(rgb.r), + g: Math.round(rgb.g), + b: Math.round(rgb.b) + }; + }; + + var rgb2hex = function(rgb) { + var hex = [ + rgb.r.toString(16), + rgb.g.toString(16), + rgb.b.toString(16) + ]; + $.each(hex, function(nr, val) { + if (val.length === 1) hex[nr] = '0' + val; + }); + return hex.join(''); + }; + + var hex2rgb = function(hex) { + hex = parseInt(((hex.indexOf('#') > -1) ? hex.substring(1) : hex), 16); + + return { + r: hex >> 16, + g: (hex & 0x00FF00) >> 8, + b: (hex & 0x0000FF) + }; + }; + + var rgb2hsb = function(rgb) { + var hsb = { h: 0, s: 0, b: 0 }; + var min = Math.min(rgb.r, rgb.g, rgb.b); + var max = Math.max(rgb.r, rgb.g, rgb.b); + var delta = max - min; + hsb.b = max; + hsb.s = max !== 0 ? 255 * delta / max : 0; + if( hsb.s !== 0 ) { + if( rgb.r === max ) { + hsb.h = (rgb.g - rgb.b) / delta; + } else if( rgb.g === max ) { + hsb.h = 2 + (rgb.b - rgb.r) / delta; + } else { + hsb.h = 4 + (rgb.r - rgb.g) / delta; + } + } else { + hsb.h = -1; + } + hsb.h *= 60; + if( hsb.h < 0 ) { + hsb.h += 360; + } + hsb.s *= 100/255; + hsb.b *= 100/255; + return hsb; + }; + + var hex2hsb = function(hex) { + var hsb = rgb2hsb(hex2rgb(hex)); + // Zero out hue marker for black, white, and grays (saturation === 0) + if( hsb.s === 0 ) hsb.h = 360; + return hsb; + }; + + var hsb2hex = function(hsb) { + return rgb2hex(hsb2rgb(hsb)); + }; + + + // Handle calls to $([selector]).miniColors() + switch(o) { + + case 'readonly': + + $(this).each( function() { + if( !$(this).hasClass('miniColors') ) return; + $(this).prop('readonly', data); + }); + + return $(this); + + case 'disabled': + + $(this).each( function() { + if( !$(this).hasClass('miniColors') ) return; + if( data ) { + disable($(this)); + } else { + enable($(this)); + } + }); + + return $(this); + + case 'value': + + // Getter + if( data === undefined ) { + if( !$(this).hasClass('miniColors') ) return; + var input = $(this), + hex = expandHex(input.val()); + return hex ? '#' + convertCase(hex, input.data('letterCase')) : null; + } + + // Setter + $(this).each( function() { + if( !$(this).hasClass('miniColors') ) return; + $(this).val(data); + setColorFromInput($(this)); + }); + + return $(this); + + case 'destroy': + + $(this).each( function() { + if( !$(this).hasClass('miniColors') ) return; + destroy($(this)); + }); + + return $(this); + + default: + + if( !o ) o = {}; + + $(this).each( function() { + + // Must be called on an input element + if( $(this)[0].tagName.toLowerCase() !== 'input' ) return; + + // If a trigger is present, the control was already created + if( $(this).data('trigger') ) return; + + // Create the control + create($(this), o, data); + + }); + + return $(this); + + } + + } + + }); + +})(jQuery); \ No newline at end of file diff --git a/3rdparty/miniColors/js/jquery.miniColors.min.js b/3rdparty/miniColors/js/jquery.miniColors.min.js new file mode 100755 index 0000000000..c00e0ace6b --- /dev/null +++ b/3rdparty/miniColors/js/jquery.miniColors.min.js @@ -0,0 +1,9 @@ +/* + * jQuery miniColors: A small color selector + * + * Copyright 2011 Cory LaViska for A Beautiful Site, LLC. (http://abeautifulsite.net/) + * + * Dual licensed under the MIT or GPL Version 2 licenses + * +*/ +if(jQuery)(function($){$.extend($.fn,{miniColors:function(o,data){var create=function(input,o,data){var color=expandHex(input.val());if(!color)color='ffffff';var hsb=hex2hsb(color);var trigger=$('');trigger.insertAfter(input);input.addClass('miniColors').data('original-maxlength',input.attr('maxlength')||null).data('original-autocomplete',input.attr('autocomplete')||null).data('letterCase','uppercase').data('trigger',trigger).data('hsb',hsb).data('change',o.change?o.change:null).data('close',o.close?o.close:null).data('open',o.open?o.open:null).attr('maxlength',7).attr('autocomplete','off').val('#'+convertCase(color,o.letterCase));if(o.readonly)input.prop('readonly',true);if(o.disabled)disable(input);trigger.bind('click.miniColors',function(event){event.preventDefault();if(input.val()==='')input.val('#');show(input)});input.bind('focus.miniColors',function(event){if(input.val()==='')input.val('#');show(input)});input.bind('blur.miniColors',function(event){var hex=expandHex(hsb2hex(input.data('hsb')));input.val(hex?'#'+convertCase(hex,input.data('letterCase')):'')});input.bind('keydown.miniColors',function(event){if(event.keyCode===9)hide(input)});input.bind('keyup.miniColors',function(event){setColorFromInput(input)});input.bind('paste.miniColors',function(event){setTimeout(function(){setColorFromInput(input)},5)})};var destroy=function(input){hide();input=$(input);input.data('trigger').remove();input.attr('autocomplete',input.data('original-autocomplete')).attr('maxlength',input.data('original-maxlength')).removeData().removeClass('miniColors').unbind('.miniColors');$(document).unbind('.miniColors')};var enable=function(input){input.prop('disabled',false).data('trigger').css('opacity',1)};var disable=function(input){hide(input);input.prop('disabled',true).data('trigger').css('opacity',0.5)};var show=function(input){if(input.prop('disabled'))return false;hide();var selector=$('
    ');selector.append('
    ').append('
    ').css({top:input.is(':visible')?input.offset().top+input.outerHeight():input.data('trigger').offset().top+input.data('trigger').outerHeight(),left:input.is(':visible')?input.offset().left:input.data('trigger').offset().left,display:'none'}).addClass(input.attr('class'));var hsb=input.data('hsb');selector.find('.miniColors-colors').css('backgroundColor','#'+hsb2hex({h:hsb.h,s:100,b:100}));var colorPosition=input.data('colorPosition');if(!colorPosition)colorPosition=getColorPositionFromHSB(hsb);selector.find('.miniColors-colorPicker').css('top',colorPosition.y+'px').css('left',colorPosition.x+'px');var huePosition=input.data('huePosition');if(!huePosition)huePosition=getHuePositionFromHSB(hsb);selector.find('.miniColors-huePicker').css('top',huePosition.y+'px');input.data('selector',selector).data('huePicker',selector.find('.miniColors-huePicker')).data('colorPicker',selector.find('.miniColors-colorPicker')).data('mousebutton',0);$('BODY').append(selector);selector.fadeIn(100);selector.bind('selectstart',function(){return false});$(document).bind('mousedown.miniColors touchstart.miniColors',function(event){input.data('mousebutton',1);var testSubject=$(event.target).parents().andSelf();if(testSubject.hasClass('miniColors-colors')){event.preventDefault();input.data('moving','colors');moveColor(input,event)}if(testSubject.hasClass('miniColors-hues')){event.preventDefault();input.data('moving','hues');moveHue(input,event)}if(testSubject.hasClass('miniColors-selector')){event.preventDefault();return}if(testSubject.hasClass('miniColors'))return;hide(input)});$(document).bind('mouseup.miniColors touchend.miniColors',function(event){event.preventDefault();input.data('mousebutton',0).removeData('moving')}).bind('mousemove.miniColors touchmove.miniColors',function(event){event.preventDefault();if(input.data('mousebutton')===1){if(input.data('moving')==='colors')moveColor(input,event);if(input.data('moving')==='hues')moveHue(input,event)}});if(input.data('open')){input.data('open').call(input.get(0),'#'+hsb2hex(hsb),hsb2rgb(hsb))}};var hide=function(input){if(!input)input='.miniColors';$(input).each(function(){var selector=$(this).data('selector');$(this).removeData('selector');$(selector).fadeOut(100,function(){if(input.data('close')){var hsb=input.data('hsb'),hex=hsb2hex(hsb);input.data('close').call(input.get(0),'#'+hex,hsb2rgb(hsb))}$(this).remove()})});$(document).unbind('.miniColors')};var moveColor=function(input,event){var colorPicker=input.data('colorPicker');colorPicker.hide();var position={x:event.pageX,y:event.pageY};if(event.originalEvent.changedTouches){position.x=event.originalEvent.changedTouches[0].pageX;position.y=event.originalEvent.changedTouches[0].pageY}position.x=position.x-input.data('selector').find('.miniColors-colors').offset().left-5;position.y=position.y-input.data('selector').find('.miniColors-colors').offset().top-5;if(position.x<=-5)position.x=-5;if(position.x>=144)position.x=144;if(position.y<=-5)position.y=-5;if(position.y>=144)position.y=144;input.data('colorPosition',position);colorPicker.css('left',position.x).css('top',position.y).show();var s=Math.round((position.x+5)*0.67);if(s<0)s=0;if(s>100)s=100;var b=100-Math.round((position.y+5)*0.67);if(b<0)b=0;if(b>100)b=100;var hsb=input.data('hsb');hsb.s=s;hsb.b=b;setColor(input,hsb,true)};var moveHue=function(input,event){var huePicker=input.data('huePicker');huePicker.hide();var position={y:event.pageY};if(event.originalEvent.changedTouches){position.y=event.originalEvent.changedTouches[0].pageY}position.y=position.y-input.data('selector').find('.miniColors-colors').offset().top-1;if(position.y<=-1)position.y=-1;if(position.y>=149)position.y=149;input.data('huePosition',position);huePicker.css('top',position.y).show();var h=Math.round((150-position.y-1)*2.4);if(h<0)h=0;if(h>360)h=360;var hsb=input.data('hsb');hsb.h=h;setColor(input,hsb,true)};var setColor=function(input,hsb,updateInput){input.data('hsb',hsb);var hex=hsb2hex(hsb);if(updateInput)input.val('#'+convertCase(hex,input.data('letterCase')));input.data('trigger').css('backgroundColor','#'+hex);if(input.data('selector'))input.data('selector').find('.miniColors-colors').css('backgroundColor','#'+hsb2hex({h:hsb.h,s:100,b:100}));if(input.data('change')){if(hex===input.data('lastChange'))return;input.data('change').call(input.get(0),'#'+hex,hsb2rgb(hsb));input.data('lastChange',hex)}};var setColorFromInput=function(input){input.val('#'+cleanHex(input.val()));var hex=expandHex(input.val());if(!hex)return false;var hsb=hex2hsb(hex);var currentHSB=input.data('hsb');if(hsb.h===currentHSB.h&&hsb.s===currentHSB.s&&hsb.b===currentHSB.b)return true;var colorPosition=getColorPositionFromHSB(hsb);var colorPicker=$(input.data('colorPicker'));colorPicker.css('top',colorPosition.y+'px').css('left',colorPosition.x+'px');input.data('colorPosition',colorPosition);var huePosition=getHuePositionFromHSB(hsb);var huePicker=$(input.data('huePicker'));huePicker.css('top',huePosition.y+'px');input.data('huePosition',huePosition);setColor(input,hsb);return true};var convertCase=function(string,letterCase){if(letterCase==='lowercase')return string.toLowerCase();if(letterCase==='uppercase')return string.toUpperCase();return string};var getColorPositionFromHSB=function(hsb){var x=Math.ceil(hsb.s/0.67);if(x<0)x=0;if(x>150)x=150;var y=150-Math.ceil(hsb.b/0.67);if(y<0)y=0;if(y>150)y=150;return{x:x-5,y:y-5}};var getHuePositionFromHSB=function(hsb){var y=150-(hsb.h/2.4);if(y<0)h=0;if(y>150)h=150;return{y:y-1}};var cleanHex=function(hex){return hex.replace(/[^A-F0-9]/ig,'')};var expandHex=function(hex){hex=cleanHex(hex);if(!hex)return null;if(hex.length===3)hex=hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2];return hex.length===6?hex:null};var hsb2rgb=function(hsb){var rgb={};var h=Math.round(hsb.h);var s=Math.round(hsb.s*255/100);var v=Math.round(hsb.b*255/100);if(s===0){rgb.r=rgb.g=rgb.b=v}else{var t1=v;var t2=(255-s)*v/255;var t3=(t1-t2)*(h%60)/60;if(h===360)h=0;if(h<60){rgb.r=t1;rgb.b=t2;rgb.g=t2+t3}else if(h<120){rgb.g=t1;rgb.b=t2;rgb.r=t1-t3}else if(h<180){rgb.g=t1;rgb.r=t2;rgb.b=t2+t3}else if(h<240){rgb.b=t1;rgb.r=t2;rgb.g=t1-t3}else if(h<300){rgb.b=t1;rgb.g=t2;rgb.r=t2+t3}else if(h<360){rgb.r=t1;rgb.g=t2;rgb.b=t1-t3}else{rgb.r=0;rgb.g=0;rgb.b=0}}return{r:Math.round(rgb.r),g:Math.round(rgb.g),b:Math.round(rgb.b)}};var rgb2hex=function(rgb){var hex=[rgb.r.toString(16),rgb.g.toString(16),rgb.b.toString(16)];$.each(hex,function(nr,val){if(val.length===1)hex[nr]='0'+val});return hex.join('')};var hex2rgb=function(hex){hex=parseInt(((hex.indexOf('#')>-1)?hex.substring(1):hex),16);return{r:hex>>16,g:(hex&0x00FF00)>>8,b:(hex&0x0000FF)}};var rgb2hsb=function(rgb){var hsb={h:0,s:0,b:0};var min=Math.min(rgb.r,rgb.g,rgb.b);var max=Math.max(rgb.r,rgb.g,rgb.b);var delta=max-min;hsb.b=max;hsb.s=max!==0?255*delta/max:0;if(hsb.s!==0){if(rgb.r===max){hsb.h=(rgb.g-rgb.b)/delta}else if(rgb.g===max){hsb.h=2+(rgb.b-rgb.r)/delta}else{hsb.h=4+(rgb.r-rgb.g)/delta}}else{hsb.h=-1}hsb.h*=60;if(hsb.h<0){hsb.h+=360}hsb.s*=100/255;hsb.b*=100/255;return hsb};var hex2hsb=function(hex){var hsb=rgb2hsb(hex2rgb(hex));if(hsb.s===0)hsb.h=360;return hsb};var hsb2hex=function(hsb){return rgb2hex(hsb2rgb(hsb))};switch(o){case'readonly':$(this).each(function(){if(!$(this).hasClass('miniColors'))return;$(this).prop('readonly',data)});return $(this);case'disabled':$(this).each(function(){if(!$(this).hasClass('miniColors'))return;if(data){disable($(this))}else{enable($(this))}});return $(this);case'value':if(data===undefined){if(!$(this).hasClass('miniColors'))return;var input=$(this),hex=expandHex(input.val());return hex?'#'+convertCase(hex,input.data('letterCase')):null}$(this).each(function(){if(!$(this).hasClass('miniColors'))return;$(this).val(data);setColorFromInput($(this))});return $(this);case'destroy':$(this).each(function(){if(!$(this).hasClass('miniColors'))return;destroy($(this))});return $(this);default:if(!o)o={};$(this).each(function(){if($(this)[0].tagName.toLowerCase()!=='input')return;if($(this).data('trigger'))return;create($(this),o,data)});return $(this)}}})})(jQuery); \ No newline at end of file diff --git a/apps/user_openid/class.openid.v3.php b/3rdparty/openid/class.openid.v3.php similarity index 99% rename from apps/user_openid/class.openid.v3.php rename to 3rdparty/openid/class.openid.v3.php index 8afb9e5b81..eeb3198665 100644 --- a/apps/user_openid/class.openid.v3.php +++ b/3rdparty/openid/class.openid.v3.php @@ -324,5 +324,3 @@ class SimpleOpenID{ } } } - -?> \ No newline at end of file diff --git a/apps/user_openid/phpmyid.php b/3rdparty/openid/phpmyid.php similarity index 99% rename from apps/user_openid/phpmyid.php rename to 3rdparty/openid/phpmyid.php index 5aaab64285..13fd31c47c 100644 --- a/apps/user_openid/phpmyid.php +++ b/3rdparty/openid/phpmyid.php @@ -1705,4 +1705,3 @@ $run_mode = (isset($_REQUEST['openid_mode']) debug("Run mode: $run_mode at: " . time()); debug($_REQUEST, 'Request params'); call_user_func($run_mode . '_mode'); -?> diff --git a/3rdparty/when/MIT-LICENSE.txt b/3rdparty/when/MIT-LICENSE.txt deleted file mode 100644 index b4429c89ac..0000000000 --- a/3rdparty/when/MIT-LICENSE.txt +++ /dev/null @@ -1,9 +0,0 @@ -License - -Copyright (c) 2010 Thomas Planer - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/3rdparty/when/When.php b/3rdparty/when/When.php deleted file mode 100644 index d54f296ed6..0000000000 --- a/3rdparty/when/When.php +++ /dev/null @@ -1,731 +0,0 @@ - - * Location: http://github.com/tplaner/When - * Created: September 2010 - * Description: Determines the next date of recursion given an iCalendar "rrule" like pattern. - * Requirements: PHP 5.3+ - makes extensive use of the Date and Time library (http://us2.php.net/manual/en/book.datetime.php) - */ -class When -{ - protected $frequency; - - protected $start_date; - protected $try_date; - - protected $end_date; - - protected $gobymonth; - protected $bymonth; - - protected $gobyweekno; - protected $byweekno; - - protected $gobyyearday; - protected $byyearday; - - protected $gobymonthday; - protected $bymonthday; - - protected $gobyday; - protected $byday; - - protected $gobysetpos; - protected $bysetpos; - - protected $suggestions; - - protected $count; - protected $counter; - - protected $goenddate; - - protected $interval; - - protected $wkst; - - protected $valid_week_days; - protected $valid_frequency; - - /** - * __construct - */ - public function __construct() - { - $this->frequency = null; - - $this->gobymonth = false; - $this->bymonth = range(1,12); - - $this->gobymonthday = false; - $this->bymonthday = range(1,31); - - $this->gobyday = false; - // setup the valid week days (0 = sunday) - $this->byday = range(0,6); - - $this->gobyyearday = false; - $this->byyearday = range(0,366); - - $this->gobysetpos = false; - $this->bysetpos = range(1,366); - - $this->gobyweekno = false; - // setup the range for valid weeks - $this->byweekno = range(0,54); - - $this->suggestions = array(); - - // this will be set if a count() is specified - $this->count = 0; - // how many *valid* results we returned - $this->counter = 0; - - // max date we'll return - $this->end_date = new DateTime('9999-12-31'); - - // the interval to increase the pattern by - $this->interval = 1; - - // what day does the week start on? (0 = sunday) - $this->wkst = 0; - - $this->valid_week_days = array('SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA'); - - $this->valid_frequency = array('SECONDLY', 'MINUTELY', 'HOURLY', 'DAILY', 'WEEKLY', 'MONTHLY', 'YEARLY'); - } - - /** - * @param DateTime|string $start_date of the recursion - also is the first return value. - * @param string $frequency of the recrusion, valid frequencies: secondly, minutely, hourly, daily, weekly, monthly, yearly - */ - public function recur($start_date, $frequency = "daily") - { - try - { - if(is_object($start_date)) - { - $this->start_date = clone $start_date; - } - else - { - // timestamps within the RFC have a 'Z' at the end of them, remove this. - $start_date = trim($start_date, 'Z'); - $this->start_date = new DateTime($start_date); - } - - $this->try_date = clone $this->start_date; - } - catch(Exception $e) - { - throw new InvalidArgumentException('Invalid start date DateTime: ' . $e); - } - - $this->freq($frequency); - - return $this; - } - - public function freq($frequency) - { - if(in_array(strtoupper($frequency), $this->valid_frequency)) - { - $this->frequency = strtoupper($frequency); - } - else - { - throw new InvalidArgumentException('Invalid frequency type.'); - } - - return $this; - } - - // accepts an rrule directly - public function rrule($rrule) - { - // strip off a trailing semi-colon - $rrule = trim($rrule, ";"); - - $parts = explode(";", $rrule); - - foreach($parts as $part) - { - list($rule, $param) = explode("=", $part); - - $rule = strtoupper($rule); - $param = strtoupper($param); - - switch($rule) - { - case "FREQ": - $this->frequency = $param; - break; - case "UNTIL": - $this->until($param); - break; - case "COUNT": - $this->count($param); - break; - case "INTERVAL": - $this->interval($param); - break; - case "BYDAY": - $params = explode(",", $param); - $this->byday($params); - break; - case "BYMONTHDAY": - $params = explode(",", $param); - $this->bymonthday($params); - break; - case "BYYEARDAY": - $params = explode(",", $param); - $this->byyearday($params); - break; - case "BYWEEKNO": - $params = explode(",", $param); - $this->byweekno($params); - break; - case "BYMONTH": - $params = explode(",", $param); - $this->bymonth($params); - break; - case "BYSETPOS": - $params = explode(",", $param); - $this->bysetpos($params); - break; - case "WKST": - $this->wkst($param); - break; - } - } - - return $this; - } - - //max number of items to return based on the pattern - public function count($count) - { - $this->count = (int)$count; - - return $this; - } - - // how often the recurrence rule repeats - public function interval($interval) - { - $this->interval = (int)$interval; - - return $this; - } - - // starting day of the week - public function wkst($day) - { - switch($day) - { - case 'SU': - $this->wkst = 0; - break; - case 'MO': - $this->wkst = 1; - break; - case 'TU': - $this->wkst = 2; - break; - case 'WE': - $this->wkst = 3; - break; - case 'TH': - $this->wkst = 4; - break; - case 'FR': - $this->wkst = 5; - break; - case 'SA': - $this->wkst = 6; - break; - } - - return $this; - } - - // max date - public function until($end_date) - { - try - { - if(is_object($end_date)) - { - $this->end_date = clone $end_date; - } - else - { - // timestamps within the RFC have a 'Z' at the end of them, remove this. - $end_date = trim($end_date, 'Z'); - $this->end_date = new DateTime($end_date); - } - } - catch(Exception $e) - { - throw new InvalidArgumentException('Invalid end date DateTime: ' . $e); - } - - return $this; - } - - public function bymonth($months) - { - if(is_array($months)) - { - $this->gobymonth = true; - $this->bymonth = $months; - } - - return $this; - } - - public function bymonthday($days) - { - if(is_array($days)) - { - $this->gobymonthday = true; - $this->bymonthday = $days; - } - - return $this; - } - - public function byweekno($weeks) - { - $this->gobyweekno = true; - - if(is_array($weeks)) - { - $this->byweekno = $weeks; - } - - return $this; - } - - public function bysetpos($days) - { - $this->gobysetpos = true; - - if(is_array($days)) - { - $this->bysetpos = $days; - } - - return $this; - } - - public function byday($days) - { - $this->gobyday = true; - - if(is_array($days)) - { - $this->byday = array(); - foreach($days as $day) - { - $len = strlen($day); - - $as = '+'; - - // 0 mean no occurence is set - $occ = 0; - - if($len == 3) - { - $occ = substr($day, 0, 1); - } - if($len == 4) - { - $as = substr($day, 0, 1); - $occ = substr($day, 1, 1); - } - - if($as == '-') - { - $occ = '-' . $occ; - } - else - { - $occ = '+' . $occ; - } - - $day = substr($day, -2, 2); - switch($day) - { - case 'SU': - $this->byday[] = $occ . 'SU'; - break; - case 'MO': - $this->byday[] = $occ . 'MO'; - break; - case 'TU': - $this->byday[] = $occ . 'TU'; - break; - case 'WE': - $this->byday[] = $occ . 'WE'; - break; - case 'TH': - $this->byday[] = $occ . 'TH'; - break; - case 'FR': - $this->byday[] = $occ . 'FR'; - break; - case 'SA': - $this->byday[] = $occ . 'SA'; - break; - } - } - } - - return $this; - } - - public function byyearday($days) - { - $this->gobyyearday = true; - - if(is_array($days)) - { - $this->byyearday = $days; - } - - return $this; - } - - // this creates a basic list of dates to "try" - protected function create_suggestions() - { - switch($this->frequency) - { - case "YEARLY": - $interval = 'year'; - break; - case "MONTHLY": - $interval = 'month'; - break; - case "WEEKLY": - $interval = 'week'; - break; - case "DAILY": - $interval = 'day'; - break; - case "HOURLY": - $interval = 'hour'; - break; - case "MINUTELY": - $interval = 'minute'; - break; - case "SECONDLY": - $interval = 'second'; - break; - } - - $month_day = $this->try_date->format('j'); - $month = $this->try_date->format('n'); - $year = $this->try_date->format('Y'); - - $timestamp = $this->try_date->format('H:i:s'); - - if($this->gobysetpos) - { - if($this->try_date == $this->start_date) - { - $this->suggestions[] = clone $this->try_date; - } - else - { - if($this->gobyday) - { - foreach($this->bysetpos as $_pos) - { - $tmp_array = array(); - $_mdays = range(1, date('t',mktime(0,0,0,$month,1,$year))); - foreach($_mdays as $_mday) - { - $date_time = new DateTime($year . '-' . $month . '-' . $_mday . ' ' . $timestamp); - - $occur = ceil($_mday / 7); - - $day_of_week = $date_time->format('l'); - $dow_abr = strtoupper(substr($day_of_week, 0, 2)); - - // set the day of the month + (positive) - $occur = '+' . $occur . $dow_abr; - $occur_zero = '+0' . $dow_abr; - - // set the day of the month - (negative) - $total_days = $date_time->format('t') - $date_time->format('j'); - $occur_neg = '-' . ceil(($total_days + 1)/7) . $dow_abr; - - $day_from_end_of_month = $date_time->format('t') + 1 - $_mday; - - if(in_array($occur, $this->byday) || in_array($occur_zero, $this->byday) || in_array($occur_neg, $this->byday)) - { - $tmp_array[] = clone $date_time; - } - } - - if($_pos > 0) - { - $this->suggestions[] = clone $tmp_array[$_pos - 1]; - } - else - { - $this->suggestions[] = clone $tmp_array[count($tmp_array) + $_pos]; - } - - } - } - } - } - elseif($this->gobyyearday) - { - foreach($this->byyearday as $_day) - { - if($_day >= 0) - { - $_day--; - - $_time = strtotime('+' . $_day . ' days', mktime(0, 0, 0, 1, 1, $year)); - $this->suggestions[] = new Datetime(date('Y-m-d', $_time) . ' ' . $timestamp); - } - else - { - $year_day_neg = 365 + $_day; - $leap_year = $this->try_date->format('L'); - if($leap_year == 1) - { - $year_day_neg = 366 + $_day; - } - - $_time = strtotime('+' . $year_day_neg . ' days', mktime(0, 0, 0, 1, 1, $year)); - $this->suggestions[] = new Datetime(date('Y-m-d', $_time) . ' ' . $timestamp); - } - } - } - // special case because for years you need to loop through the months too - elseif($this->gobyday && $interval == "year") - { - foreach($this->bymonth as $_month) - { - // this creates an array of days of the month - $_mdays = range(1, date('t',mktime(0,0,0,$_month,1,$year))); - foreach($_mdays as $_mday) - { - $date_time = new DateTime($year . '-' . $_month . '-' . $_mday . ' ' . $timestamp); - - // get the week of the month (1, 2, 3, 4, 5, etc) - $week = $date_time->format('W'); - - if($date_time >= $this->start_date && in_array($week, $this->byweekno)) - { - $this->suggestions[] = clone $date_time; - } - } - } - } - elseif($interval == "day") - { - $this->suggestions[] = clone $this->try_date; - } - elseif($interval == "week") - { - $this->suggestions[] = clone $this->try_date; - - if($this->gobyday) - { - $week_day = $this->try_date->format('w'); - - $days_in_month = $this->try_date->format('t'); - - $overflow_count = 1; - $_day = $month_day; - - $run = true; - while($run) - { - $_day++; - if($_day <= $days_in_month) - { - $tmp_date = new DateTime($year . '-' . $month . '-' . $_day . ' ' . $timestamp); - } - else - { - //$tmp_month = $month+1; - $tmp_date = new DateTime($year . '-' . $month . '-' . $overflow_count . ' ' . $timestamp); - $tmp_date->modify('+1 month'); - $overflow_count++; - } - - $week_day = $tmp_date->format('w'); - - if($this->try_date == $this->start_date) - { - if($week_day == $this->wkst) - { - $this->try_date = clone $tmp_date; - $this->try_date->modify('-7 days'); - $run = false; - } - } - - if($week_day != $this->wkst) - { - $this->suggestions[] = clone $tmp_date; - } - else - { - $run = false; - } - } - } - } - elseif($this->gobyday && $interval == "month") - { - $_mdays = range(1, date('t',mktime(0,0,0,$month,1,$year))); - foreach($_mdays as $_mday) - { - $date_time = new DateTime($year . '-' . $month . '-' . $_mday . ' ' . $timestamp); - - // get the week of the month (1, 2, 3, 4, 5, etc) - $week = $date_time->format('W'); - - if($date_time >= $this->start_date && in_array($week, $this->byweekno)) - { - $this->suggestions[] = clone $date_time; - } - } - } - elseif($this->gobymonth) - { - foreach($this->bymonth as $_month) - { - $date_time = new DateTime($year . '-' . $_month . '-' . $month_day . ' ' . $timestamp); - - if($date_time >= $this->start_date) - { - $this->suggestions[] = clone $date_time; - } - } - } - else - { - $this->suggestions[] = clone $this->try_date; - } - - if($interval == "month") - { - - $this->try_date->modify('first day of next month'); - if((int) date('t', $this->try_date->format('U')) > (int) $this->start_date->format('j')){ - $this->try_date->modify('+' . (int) $this->start_date->format('j') - 1 . ' day'); - }else{ - $this->try_date->modify('+' . (int) date('t', $this->try_date->format('U')) - 1 . ' day'); - } - } - else - { - $this->try_date->modify($this->interval . ' ' . $interval); - } - } - - protected function valid_date($date) - { - $year = $date->format('Y'); - $month = $date->format('n'); - $day = $date->format('j'); - - $year_day = $date->format('z') + 1; - - $year_day_neg = -366 + $year_day; - $leap_year = $date->format('L'); - if($leap_year == 1) - { - $year_day_neg = -367 + $year_day; - } - - // this is the nth occurence of the date - $occur = ceil($day / 7); - - $week = $date->format('W'); - - $day_of_week = $date->format('l'); - $dow_abr = strtoupper(substr($day_of_week, 0, 2)); - - // set the day of the month + (positive) - $occur = '+' . $occur . $dow_abr; - $occur_zero = '+0' . $dow_abr; - - // set the day of the month - (negative) - $total_days = $date->format('t') - $date->format('j'); - $occur_neg = '-' . ceil(($total_days + 1)/7) . $dow_abr; - - $day_from_end_of_month = $date->format('t') + 1 - $day; - - if(in_array($month, $this->bymonth) && - (in_array($occur, $this->byday) || in_array($occur_zero, $this->byday) || in_array($occur_neg, $this->byday)) && - in_array($week, $this->byweekno) && - (in_array($day, $this->bymonthday) || in_array(-$day_from_end_of_month, $this->bymonthday)) && - (in_array($year_day, $this->byyearday) || in_array($year_day_neg, $this->byyearday))) - { - return true; - } - else - { - return false; - } - } - - // return the next valid DateTime object which matches the pattern and follows the rules - public function next() - { - // check the counter is set - if($this->count !== 0) - { - if($this->counter >= $this->count) - { - return false; - } - } - - // create initial set of suggested dates - if(count($this->suggestions) === 0) - { - $this->create_suggestions(); - } - - // loop through the suggested dates - while(count($this->suggestions) > 0) - { - // get the first one on the array - $try_date = array_shift($this->suggestions); - - // make sure the date doesn't exceed the max date - if($try_date > $this->end_date) - { - return false; - } - - // make sure it falls within the allowed days - if($this->valid_date($try_date) === true) - { - $this->counter++; - return $try_date; - } - else - { - // we might be out of suggested days, so load some more - if(count($this->suggestions) === 0) - { - $this->create_suggestions(); - } - } - } - } -} diff --git a/apps/admin_audit/appinfo/app.php b/apps/admin_audit/appinfo/app.php new file mode 100644 index 0000000000..e52f633cf1 --- /dev/null +++ b/apps/admin_audit/appinfo/app.php @@ -0,0 +1,18 @@ + + + admin_audit + Log audit info + 0.1 + AGPL + Bart Visscher + 4 + Audit user actions in Owncloud + true + diff --git a/apps/admin_audit/lib/hooks_handlers.php b/apps/admin_audit/lib/hooks_handlers.php new file mode 100644 index 0000000000..c5aec97d93 --- /dev/null +++ b/apps/admin_audit/lib/hooks_handlers.php @@ -0,0 +1,72 @@ + 14, - 'id' => 'admin_dependencies_chk', - 'name' => 'Owncloud Install Info' )); - OCP\App::registerAdmin('admin_dependencies_chk','settings'); diff --git a/apps/admin_dependencies_chk/l10n/.gitkeep b/apps/admin_dependencies_chk/l10n/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apps/admin_dependencies_chk/l10n/ca.php b/apps/admin_dependencies_chk/l10n/ca.php new file mode 100644 index 0000000000..08f4ec8078 --- /dev/null +++ b/apps/admin_dependencies_chk/l10n/ca.php @@ -0,0 +1,14 @@ + "El mòdul php-json és necessari per moltes aplicacions per comunicacions internes", +"The php-curl modude is needed to fetch the page title when adding a bookmarks" => "El mòdul php-curl és necessari per mostrar el títol de la pàgina quan s'afegeixen adreces d'interès", +"The php-gd module is needed to create thumbnails of your images" => "El mòdul php-gd és necessari per generar miniatures d'imatges", +"The php-ldap module is needed connect to your ldap server" => "El mòdul php-ldap és necessari per connectar amb el servidor ldap", +"The php-zip module is needed download multiple files at once" => "El mòdul php-zip és necessari per baixar múltiples fitxers de cop", +"The php-mb_multibyte module is needed to manage correctly the encoding." => "El mòdul php-mb_multibyte és necessari per gestionar correctament la codificació.", +"The php-ctype module is needed validate data." => "El mòdul php-ctype és necessari per validar dades.", +"The php-xml module is needed to share files with webdav." => "El mòdul php-xml és necessari per compatir els fitxers amb webdav.", +"The allow_url_fopen directive of your php.ini should be set to 1 to retrieve knowledge base from OCS servers" => "La directiva allow_url_fopen de php.ini hauria d'establir-se en 1 per accedir a la base de coneixements dels servidors OCS", +"The php-pdo module is needed to store owncloud data into a database." => "El mòdul php-pdo és necessari per desar les dades d'ownCloud en una base de dades.", +"Dependencies status" => "Estat de dependències", +"Used by :" => "Usat per:" +); diff --git a/apps/admin_dependencies_chk/l10n/cs_CZ.php b/apps/admin_dependencies_chk/l10n/cs_CZ.php new file mode 100644 index 0000000000..f750f13aef --- /dev/null +++ b/apps/admin_dependencies_chk/l10n/cs_CZ.php @@ -0,0 +1,14 @@ + "Modul php-json je třeba pro vzájemnou komunikaci mnoha aplikací", +"The php-curl modude is needed to fetch the page title when adding a bookmarks" => "Modul php-curl je třeba pro zobrazení titulu strany v okamžiku přidání záložky", +"The php-gd module is needed to create thumbnails of your images" => "Modul php-gd je třeba pro tvorbu náhledů Vašich obrázků", +"The php-ldap module is needed connect to your ldap server" => "Modul php-ldap je třeba pro připojení na Váš ldap server", +"The php-zip module is needed download multiple files at once" => "Modul php-zip je třeba pro souběžné stahování souborů", +"The php-mb_multibyte module is needed to manage correctly the encoding." => "Modul php-mb_multibyte je třeba pro správnou funkci kódování.", +"The php-ctype module is needed validate data." => "Modul php-ctype je třeba k ověřování dat.", +"The php-xml module is needed to share files with webdav." => "Modul php-xml je třeba ke sdílení souborů prostřednictvím WebDAV.", +"The allow_url_fopen directive of your php.ini should be set to 1 to retrieve knowledge base from OCS servers" => "Příkaz allow_url_fopen ve Vašem php.ini souboru by měl být nastaven na 1 kvůli získávání informací z OCS serverů", +"The php-pdo module is needed to store owncloud data into a database." => "Modul php-pdo je třeba pro ukládání dat ownCloud do databáze", +"Dependencies status" => "Status závislostí", +"Used by :" => "Používáno:" +); diff --git a/apps/admin_dependencies_chk/l10n/de.php b/apps/admin_dependencies_chk/l10n/de.php new file mode 100644 index 0000000000..7877e7d679 --- /dev/null +++ b/apps/admin_dependencies_chk/l10n/de.php @@ -0,0 +1,14 @@ + "Das Modul php-json wird von vielen Anwendungen zur internen Kommunikation benötigt.", +"The php-curl modude is needed to fetch the page title when adding a bookmarks" => "Das Modul php-curl wird benötigt, um den Titel der Seite für die Lesezeichen hinzuzufügen.", +"The php-gd module is needed to create thumbnails of your images" => "Das Modul php-gd wird für die Erzeugung der Vorschaubilder benötigt.", +"The php-ldap module is needed connect to your ldap server" => "Das Modul php-ldap wird für die Verbindung mit dem LDAP-Server benötigt.", +"The php-zip module is needed download multiple files at once" => "Das Modul php-zip wird für den gleichzeitigen Download mehrerer Dateien benötigt.", +"The php-mb_multibyte module is needed to manage correctly the encoding." => "Das Modul php_mb_multibyte wird benötigt, um das Encoding richtig zu handhaben.", +"The php-ctype module is needed validate data." => "Das Modul php-ctype wird benötigt, um Daten zu prüfen.", +"The php-xml module is needed to share files with webdav." => "Das Modul php-xml wird benötigt, um Dateien über WebDAV zu teilen.", +"The allow_url_fopen directive of your php.ini should be set to 1 to retrieve knowledge base from OCS servers" => "Die Richtlinie allow_url_fopen in Ihrer php.ini sollte auf 1 gesetzt werden, um die Wissensbasis vom OCS-Server abrufen.", +"The php-pdo module is needed to store owncloud data into a database." => "Das Modul php-pdo wird benötigt, um Daten in der Datenbank zu speichern.", +"Dependencies status" => "Status der Abhängigkeiten", +"Used by :" => "Benutzt von:" +); diff --git a/apps/admin_dependencies_chk/l10n/el.php b/apps/admin_dependencies_chk/l10n/el.php new file mode 100644 index 0000000000..9dd455b670 --- /dev/null +++ b/apps/admin_dependencies_chk/l10n/el.php @@ -0,0 +1,4 @@ + "Κατάσταση εξαρτήσεων", +"Used by :" => "Χρησιμοποιήθηκε από:" +); diff --git a/apps/admin_dependencies_chk/l10n/eo.php b/apps/admin_dependencies_chk/l10n/eo.php new file mode 100644 index 0000000000..65896831f7 --- /dev/null +++ b/apps/admin_dependencies_chk/l10n/eo.php @@ -0,0 +1,14 @@ + "La modulo php-json necesas por komuniko inter la multaj aplikaĵoj", +"The php-curl modude is needed to fetch the page title when adding a bookmarks" => "La modulo php-curl necesas por venigi la paĝotitolon dum aldono de legosigno", +"The php-gd module is needed to create thumbnails of your images" => "La modulo php-gd necesas por krei bildetojn.", +"The php-ldap module is needed connect to your ldap server" => "La modulo php-ldap necesas por konekti al via LDAP-servilo.", +"The php-zip module is needed download multiple files at once" => "La modulo php-zip necesas por elŝuti plurajn dosierojn per unu fojo.", +"The php-mb_multibyte module is needed to manage correctly the encoding." => "La modulo php-mb_multibyte necesas por ĝuste administri la kodprezenton.", +"The php-ctype module is needed validate data." => "La modulo php-ctype necesas por validkontroli datumojn.", +"The php-xml module is needed to share files with webdav." => "La modulo php-xml necesas por kunhavigi dosierojn per WebDAV.", +"The allow_url_fopen directive of your php.ini should be set to 1 to retrieve knowledge base from OCS servers" => "La ordono allow_url_fopen de via php.ini devus valori 1 por ricevi scibazon el OCS-serviloj", +"The php-pdo module is needed to store owncloud data into a database." => "La modulo php-pdo necesas por konservi datumojn de ownCloud en datumbazo.", +"Dependencies status" => "Stato de dependoj", +"Used by :" => "Uzata de:" +); diff --git a/apps/admin_dependencies_chk/l10n/es.php b/apps/admin_dependencies_chk/l10n/es.php new file mode 100644 index 0000000000..cbf13951ac --- /dev/null +++ b/apps/admin_dependencies_chk/l10n/es.php @@ -0,0 +1,4 @@ + "Estado de las dependencias", +"Used by :" => "Usado por:" +); diff --git a/apps/admin_dependencies_chk/l10n/et_EE.php b/apps/admin_dependencies_chk/l10n/et_EE.php new file mode 100644 index 0000000000..529c8e78d1 --- /dev/null +++ b/apps/admin_dependencies_chk/l10n/et_EE.php @@ -0,0 +1,14 @@ + "php-json moodul on vajalik paljude rakenduse poolt omvahelise suhtlemise jaoks", +"The php-curl modude is needed to fetch the page title when adding a bookmarks" => "php-curl moodul on vajalik lehe pealkirja tõmbamiseks järjehoidja lisamisel", +"The php-gd module is needed to create thumbnails of your images" => "php-gd moodul on vajalik sinu piltidest pisipiltide loomiseks", +"The php-ldap module is needed connect to your ldap server" => "php-ldap moodul on vajalik sinu ldap serveriga ühendumiseks", +"The php-zip module is needed download multiple files at once" => "php-zip moodul on vajalik mitme faili korraga alla laadimiseks", +"The php-mb_multibyte module is needed to manage correctly the encoding." => "php-mb_multibyte moodul on vajalik kodeerimise korrektseks haldamiseks.", +"The php-ctype module is needed validate data." => "php-ctype moodul on vajalik andmete kontrollimiseks.", +"The php-xml module is needed to share files with webdav." => "php-xml moodul on vajalik failide jagamiseks webdav-iga.", +"The allow_url_fopen directive of your php.ini should be set to 1 to retrieve knowledge base from OCS servers" => "Sinu php.ini failis oleva direktiivi allow_url_fopen väärtuseks peaks määrama 1, et saaks tõmmata teadmistebaasi OCS-i serveritest", +"The php-pdo module is needed to store owncloud data into a database." => "php-pdo moodul on vajalik owncloudi andmete salvestamiseks andmebaasi.", +"Dependencies status" => "Sõltuvuse staatus", +"Used by :" => "Kasutab :" +); diff --git a/apps/admin_dependencies_chk/l10n/fi_FI.php b/apps/admin_dependencies_chk/l10n/fi_FI.php new file mode 100644 index 0000000000..85e33cfe8c --- /dev/null +++ b/apps/admin_dependencies_chk/l10n/fi_FI.php @@ -0,0 +1,9 @@ + "php-gd-moduuli vaaditaan, jotta kuvista on mahdollista luoda esikatselukuvia", +"The php-ldap module is needed connect to your ldap server" => "php-ldap-moduuli vaaditaan, jotta yhteys ldap-palvelimeen on mahdollista", +"The php-zip module is needed download multiple files at once" => "php-zip-moduuli vaaditaan, jotta useiden tiedostojen samanaikainen lataus on mahdollista", +"The php-xml module is needed to share files with webdav." => "php-xml-moduuli vaaditaan, jotta tiedostojen jako webdavia käyttäen on mahdollista", +"The php-pdo module is needed to store owncloud data into a database." => "php-pdo-moduuli tarvitaan, jotta ownCloud-tietojen tallennus tietokantaan on mahdollista", +"Dependencies status" => "Riippuvuuksien tila", +"Used by :" => "Käyttökohde:" +); diff --git a/apps/admin_dependencies_chk/l10n/fr.php b/apps/admin_dependencies_chk/l10n/fr.php new file mode 100644 index 0000000000..1b195b78bb --- /dev/null +++ b/apps/admin_dependencies_chk/l10n/fr.php @@ -0,0 +1,14 @@ + "Le module php-json est requis pour l'inter-communication de nombreux modules.", +"The php-curl modude is needed to fetch the page title when adding a bookmarks" => "Le module php-curl est requis afin de rapatrier le titre des pages lorsque vous ajoutez un marque-pages.", +"The php-gd module is needed to create thumbnails of your images" => "Le module php-gd est requis afin de permettre la création d'aperçus pour vos images.", +"The php-ldap module is needed connect to your ldap server" => "Le module php-ldap est requis afin de permettre la connexion à votre serveur ldap.", +"The php-zip module is needed download multiple files at once" => "Le module php-zip est requis pour le téléchargement simultané de plusieurs fichiers.", +"The php-mb_multibyte module is needed to manage correctly the encoding." => "Le module php-mb_multibyte est requis pour une gestion correcte des encodages.", +"The php-ctype module is needed validate data." => "Le module php-ctype est requis pour la validation des données.", +"The php-xml module is needed to share files with webdav." => "Le module php-xml est requis pour le partage de fichiers via webdav.", +"The allow_url_fopen directive of your php.ini should be set to 1 to retrieve knowledge base from OCS servers" => "La directive allow_url_fopen de votre fichier php.ini doit être à la valeur 1 afin de permettre le rapatriement de la base de connaissance depuis les serveurs OCS.", +"The php-pdo module is needed to store owncloud data into a database." => "le module php-pdo est requis pour le stockage des données ownCloud en base de données.", +"Dependencies status" => "Statut des dépendances", +"Used by :" => "Utilisé par :" +); diff --git a/apps/admin_dependencies_chk/l10n/it.php b/apps/admin_dependencies_chk/l10n/it.php new file mode 100644 index 0000000000..f51286966e --- /dev/null +++ b/apps/admin_dependencies_chk/l10n/it.php @@ -0,0 +1,14 @@ + "Il modulo php-json è richiesto per l'intercomunicazione di diverse applicazioni", +"The php-curl modude is needed to fetch the page title when adding a bookmarks" => "Il modulo php-curl è richiesto per scaricare il titolo della pagina quando si aggiunge un segnalibro", +"The php-gd module is needed to create thumbnails of your images" => "Il modulo php-gd è richiesto per creare miniature delle tue immagini", +"The php-ldap module is needed connect to your ldap server" => "Il modulo php-ldap è richiesto per collegarsi a un server ldap", +"The php-zip module is needed download multiple files at once" => "Il modulo php-zip è richiesto per scaricare diversi file contemporaneamente", +"The php-mb_multibyte module is needed to manage correctly the encoding." => "Il modulo php-mb_multibyte è richiesto per gestire correttamente la codifica.", +"The php-ctype module is needed validate data." => "Il modulo php-ctype è richiesto per la validazione dei dati.", +"The php-xml module is needed to share files with webdav." => "Il modulo php-xml è richiesto per condividere i file con webdav.", +"The allow_url_fopen directive of your php.ini should be set to 1 to retrieve knowledge base from OCS servers" => "La direttiva allow_url_fopen del tuo php.ini deve essere impostata a 1 per recuperare la base di conoscenza dai server di OCS", +"The php-pdo module is needed to store owncloud data into a database." => "Il modulo php-pdo è richiesto per archiviare i dati di ownCloud in un database.", +"Dependencies status" => "Stato delle dipendenze", +"Used by :" => "Usato da:" +); diff --git a/apps/admin_dependencies_chk/l10n/ja_JP.php b/apps/admin_dependencies_chk/l10n/ja_JP.php new file mode 100644 index 0000000000..0770b92db1 --- /dev/null +++ b/apps/admin_dependencies_chk/l10n/ja_JP.php @@ -0,0 +1,14 @@ + "php-jsonモジュールはアプリケーション間の内部通信に必要です", +"The php-curl modude is needed to fetch the page title when adding a bookmarks" => "php-curlモジュールはブックマーク追加時のページタイトル取得に必要です", +"The php-gd module is needed to create thumbnails of your images" => "php-gdモジュールはサムネイル画像の生成に必要です", +"The php-ldap module is needed connect to your ldap server" => "php-ldapモジュールはLDAPサーバへの接続に必要です", +"The php-zip module is needed download multiple files at once" => "php-zipモジュールは複数ファイルの同時ダウンロードに必要です", +"The php-mb_multibyte module is needed to manage correctly the encoding." => "php-mb_multibyteモジュールはエンコードを正しく扱うために必要です", +"The php-ctype module is needed validate data." => "php-ctypeモジュールはデータのバリデーションに必要です", +"The php-xml module is needed to share files with webdav." => "php-xmlモジュールはWebDAVでのファイル共有に必要です", +"The allow_url_fopen directive of your php.ini should be set to 1 to retrieve knowledge base from OCS servers" => "php.iniのallow_url_fopenはOCSサーバから知識ベースを取得するために1に設定しなくてはなりません", +"The php-pdo module is needed to store owncloud data into a database." => "php-pdoモジュールはデータベースにownCloudのデータを格納するために必要です", +"Dependencies status" => "依存関係の状況", +"Used by :" => "利用先 :" +); diff --git a/apps/admin_dependencies_chk/l10n/lt_LT.php b/apps/admin_dependencies_chk/l10n/lt_LT.php new file mode 100644 index 0000000000..4fed2ab93d --- /dev/null +++ b/apps/admin_dependencies_chk/l10n/lt_LT.php @@ -0,0 +1,14 @@ + "Php-json modulis yra reikalingas duomenų keitimuisi tarp programų", +"The php-curl modude is needed to fetch the page title when adding a bookmarks" => "Php-curl modulis automatiškai nuskaito tinklapio pavadinimą kuomet išsaugoma žymelė.", +"The php-gd module is needed to create thumbnails of your images" => "Php-gd modulis yra naudojamas paveikslėlių miniatiūroms kurti.", +"The php-ldap module is needed connect to your ldap server" => "Php-ldap modulis yra reikalingas prisijungimui prie jūsų ldap serverio", +"The php-zip module is needed download multiple files at once" => "Php-zip modulis yra reikalingas kelių failų atsiuntimui iš karto.", +"The php-mb_multibyte module is needed to manage correctly the encoding." => "Php-mb_multibyte modulis yra naudojamas apdoroti įvairius teksto kodavimo formatus.", +"The php-ctype module is needed validate data." => "Php-ctype modulis yra reikalingas duomenų tikrinimui.", +"The php-xml module is needed to share files with webdav." => "Php-xml modulis yra reikalingas failų dalinimuisi naudojant webdav.", +"The allow_url_fopen directive of your php.ini should be set to 1 to retrieve knowledge base from OCS servers" => "allow_url_fopen direktyva turėtų būti nustatyta į \"1\" jei norite automatiškai gauti žinių bazės informaciją iš OCS serverių.", +"The php-pdo module is needed to store owncloud data into a database." => "Php-pdo modulis yra reikalingas duomenų saugojimui į owncloud duomenų bazę.", +"Dependencies status" => "Priklausomybės", +"Used by :" => "Naudojama:" +); diff --git a/apps/admin_dependencies_chk/l10n/pl.php b/apps/admin_dependencies_chk/l10n/pl.php new file mode 100644 index 0000000000..e40d23cb7e --- /dev/null +++ b/apps/admin_dependencies_chk/l10n/pl.php @@ -0,0 +1,14 @@ + "Moduł php-json jest wymagane przez wiele aplikacji do wewnętrznej łączności", +"The php-curl modude is needed to fetch the page title when adding a bookmarks" => "Modude php-curl jest wymagany do pobrania tytułu strony podczas dodawania zakładki", +"The php-gd module is needed to create thumbnails of your images" => "Moduł php-gd jest wymagany do tworzenia miniatury obrazów", +"The php-ldap module is needed connect to your ldap server" => "Moduł php-ldap jest wymagany aby połączyć się z serwerem ldap", +"The php-zip module is needed download multiple files at once" => "Moduł php-zip jest wymagany aby pobrać wiele plików na raz", +"The php-mb_multibyte module is needed to manage correctly the encoding." => "Moduł php-mb_multibyte jest wymagany do poprawnego zarządzania kodowaniem.", +"The php-ctype module is needed validate data." => "Moduł php-ctype jest wymagany do sprawdzania poprawności danych.", +"The php-xml module is needed to share files with webdav." => "Moduł php-xml jest wymagany do udostępniania plików przy użyciu protokołu webdav.", +"The allow_url_fopen directive of your php.ini should be set to 1 to retrieve knowledge base from OCS servers" => "Dyrektywy allow_url_fopen użytkownika php.ini powinna być ustawiona na 1 do pobierania bazy wiedzy z serwerów OCS", +"The php-pdo module is needed to store owncloud data into a database." => "Moduł php-pdo jest wymagany do przechowywania danych owncloud w bazie danych.", +"Dependencies status" => "Stan zależności", +"Used by :" => "Używane przez:" +); diff --git a/apps/admin_dependencies_chk/l10n/sl.php b/apps/admin_dependencies_chk/l10n/sl.php new file mode 100644 index 0000000000..0d36aa379c --- /dev/null +++ b/apps/admin_dependencies_chk/l10n/sl.php @@ -0,0 +1,14 @@ + "Modul php-json je potreben za medsebojno komunikacijo veliko aplikacij.", +"The php-curl modude is needed to fetch the page title when adding a bookmarks" => "Modul php-curl je potreben za pridobivanje naslova strani pri dodajanju zaznamkov.", +"The php-gd module is needed to create thumbnails of your images" => "Modul php-gd je potreben za ustvarjanje sličic za predogled.", +"The php-ldap module is needed connect to your ldap server" => "Modul php-ldap je potreben za povezavo z vašim ldap strežnikom.", +"The php-zip module is needed download multiple files at once" => "Modul php-zip je potreben za prenašanje večih datotek hkrati.", +"The php-mb_multibyte module is needed to manage correctly the encoding." => "Modul php-mb_multibyte je potreben za pravilno upravljanje kodiranja.", +"The php-ctype module is needed validate data." => "Modul php-ctype je potreben za preverjanje veljavnosti podatkov.", +"The php-xml module is needed to share files with webdav." => "Modul php-xml je potreben za izmenjavo datotek preko protokola WebDAV.", +"The allow_url_fopen directive of your php.ini should be set to 1 to retrieve knowledge base from OCS servers" => "Direktiva allow_url_fopen v vaši php.ini datoteki mora biti nastavljena na 1, če želite omogočiti dostop do zbirke znanja na strežnikih OCS.", +"The php-pdo module is needed to store owncloud data into a database." => "Modul php-pdo je potreben za shranjevanje ownCloud podatkov v podatkovno zbirko.", +"Dependencies status" => "Stanje odvisnosti", +"Used by :" => "Uporablja:" +); diff --git a/apps/admin_dependencies_chk/l10n/sv.php b/apps/admin_dependencies_chk/l10n/sv.php new file mode 100644 index 0000000000..07868f3c03 --- /dev/null +++ b/apps/admin_dependencies_chk/l10n/sv.php @@ -0,0 +1,14 @@ + "Modulen php-json behövs av många applikationer som interagerar.", +"The php-curl modude is needed to fetch the page title when adding a bookmarks" => "Modulen php-curl behövs för att hämta sidans titel när du lägger till bokmärken.", +"The php-gd module is needed to create thumbnails of your images" => "Modulen php-gd behövs för att skapa miniatyrer av dina bilder.", +"The php-ldap module is needed connect to your ldap server" => "Modulen php-ldap behövs för att ansluta mot din ldapserver.", +"The php-zip module is needed download multiple files at once" => "Modulen php-zip behövs för att kunna ladda ner flera filer på en gång.", +"The php-mb_multibyte module is needed to manage correctly the encoding." => "Modulen php-mb_multibyte behövs för att hantera korrekt teckenkodning.", +"The php-ctype module is needed validate data." => "Modulen php-ctype behövs för att validera data.", +"The php-xml module is needed to share files with webdav." => "Modulen php-xml behövs för att kunna dela filer med webdav.", +"The allow_url_fopen directive of your php.ini should be set to 1 to retrieve knowledge base from OCS servers" => "Direktivet allow_url_fopen i php.ini bör sättas till 1 för att kunna hämta kunskapsbasen från OCS-servrar.", +"The php-pdo module is needed to store owncloud data into a database." => "Modulen php-pdo behövs för att kunna lagra ownCloud data i en databas.", +"Dependencies status" => "Beroenden status", +"Used by :" => "Används av:" +); diff --git a/apps/admin_dependencies_chk/l10n/th_TH.php b/apps/admin_dependencies_chk/l10n/th_TH.php new file mode 100644 index 0000000000..01d5c36366 --- /dev/null +++ b/apps/admin_dependencies_chk/l10n/th_TH.php @@ -0,0 +1,14 @@ + "โมดูล php-json จำเป็นต้องใช้สำหรับแอพพลิเคชั่นหลายๆตัวเพื่อการเชื่อมต่อสากล", +"The php-curl modude is needed to fetch the page title when adding a bookmarks" => "โมดูล php-curl จำเป็นต้องใช้สำหรับดึงข้อมูลชื่อหัวเว็บเมื่อเพิ่มเข้าไปยังรายการโปรด", +"The php-gd module is needed to create thumbnails of your images" => "โมดูล php-gd จำเป็นต้องใช้สำหรับสร้างรูปภาพขนาดย่อของรูปภาพของคุณ", +"The php-ldap module is needed connect to your ldap server" => "โมดูล php-ldap จำเป็นต้องใช้สำหรับการเชื่อมต่อกับเซิร์ฟเวอร์ ldap ของคุณ", +"The php-zip module is needed download multiple files at once" => "โมดูล php-zip จำเป็นต้องใช้สำหรับดาวน์โหลดไฟล์พร้อมกันหลายๆไฟล์ในครั้งเดียว", +"The php-mb_multibyte module is needed to manage correctly the encoding." => "โมดูล php-mb_multibyte จำเป็นต้องใช้สำหรับการจัดการการแปลงรหัสไฟล์อย่างถูกต้อง", +"The php-ctype module is needed validate data." => "โมดูล php-ctype จำเป็นต้องใช้สำหรับตรวจสอบความถูกต้องของข้อมูล", +"The php-xml module is needed to share files with webdav." => "โมดูล php-xml จำเป็นต้องใช้สำหรับแชร์ไฟล์ด้วย webdav", +"The allow_url_fopen directive of your php.ini should be set to 1 to retrieve knowledge base from OCS servers" => "คำสั่ง allow_url_fopen ที่อยู่ในไฟล์ php.ini ของคุณ ควรกำหนดเป็น 1 เพื่อดึงข้อมูลของฐานความรู้ต่างๆจากเซิร์ฟเวอร์ของ OCS", +"The php-pdo module is needed to store owncloud data into a database." => "โมดูล php-pdo จำเป็นต้องใช้สำหรับจัดเก็บข้อมูลใน owncloud เข้าไปไว้ยังฐานข้อมูล", +"Dependencies status" => "สถานะการอ้างอิง", +"Used by :" => "ใช้งานโดย:" +); diff --git a/apps/admin_migrate/l10n/.gitkeep b/apps/admin_migrate/l10n/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apps/admin_migrate/l10n/ca.php b/apps/admin_migrate/l10n/ca.php new file mode 100644 index 0000000000..8743b39760 --- /dev/null +++ b/apps/admin_migrate/l10n/ca.php @@ -0,0 +1,5 @@ + "Exporta aquesta instància de ownCloud", +"This will create a compressed file that contains the data of this owncloud instance.\n Please choose the export type:" => "Això crearà un fitxer comprimit amb les dades d'aquesta instància ownCloud.\n Escolliu el tipus d'exportació:", +"Export" => "Exporta" +); diff --git a/apps/admin_migrate/l10n/cs_CZ.php b/apps/admin_migrate/l10n/cs_CZ.php new file mode 100644 index 0000000000..0dc1a61d5d --- /dev/null +++ b/apps/admin_migrate/l10n/cs_CZ.php @@ -0,0 +1,5 @@ + "Export této instance ownCloud", +"This will create a compressed file that contains the data of this owncloud instance.\n Please choose the export type:" => "Bude vytvořen komprimovaný soubor obsahující data této instance ownCloud.⏎ Zvolte typ exportu:", +"Export" => "Export" +); diff --git a/apps/admin_migrate/l10n/da.php b/apps/admin_migrate/l10n/da.php new file mode 100644 index 0000000000..a33635cdc1 --- /dev/null +++ b/apps/admin_migrate/l10n/da.php @@ -0,0 +1,4 @@ + "Eksporter ownCloud instans", +"Export" => "Eksporter" +); diff --git a/apps/admin_migrate/l10n/de.php b/apps/admin_migrate/l10n/de.php new file mode 100644 index 0000000000..d42179b75a --- /dev/null +++ b/apps/admin_migrate/l10n/de.php @@ -0,0 +1,5 @@ + "Diese ownCloud-Instanz exportieren.", +"This will create a compressed file that contains the data of this owncloud instance.\n Please choose the export type:" => "Dies wird eine komprimierte Datei erzeugen, welche die Daten dieser ownCloud-Instanz enthält.\n Bitte wählen Sie den Exporttyp:", +"Export" => "Exportieren" +); diff --git a/apps/admin_migrate/l10n/el.php b/apps/admin_migrate/l10n/el.php new file mode 100644 index 0000000000..95034c46a4 --- /dev/null +++ b/apps/admin_migrate/l10n/el.php @@ -0,0 +1,4 @@ + "Αυτό θα δημιουργήσει ένα συμπιεσμένο αρχείο που θα περιέχει τα δεδομένα από αυτό το ownCloud.\n Παρακαλώ επιλέξτε τον τύπο εξαγωγής:", +"Export" => "Εξαγωγή" +); diff --git a/apps/admin_migrate/l10n/eo.php b/apps/admin_migrate/l10n/eo.php new file mode 100644 index 0000000000..a37be76425 --- /dev/null +++ b/apps/admin_migrate/l10n/eo.php @@ -0,0 +1,5 @@ + "Malenporti ĉi tiun aperon de ownCloud", +"This will create a compressed file that contains the data of this owncloud instance.\n Please choose the export type:" => "Ĉi tio kreos densigitan dosieron, kiu enhavos la datumojn de ĉi tiu apero de ownCloud.\nBonvolu elekti la tipon de malenportado:", +"Export" => "Malenporti" +); diff --git a/apps/admin_migrate/l10n/es.php b/apps/admin_migrate/l10n/es.php new file mode 100644 index 0000000000..cb6699b1d9 --- /dev/null +++ b/apps/admin_migrate/l10n/es.php @@ -0,0 +1,5 @@ + "Exportar esta instancia de ownCloud", +"This will create a compressed file that contains the data of this owncloud instance.\n Please choose the export type:" => "Se creará un archivo comprimido que contendrá los datos de esta instancia de owncloud.\n Por favor elegir el tipo de exportación:", +"Export" => "Exportar" +); diff --git a/apps/admin_migrate/l10n/et_EE.php b/apps/admin_migrate/l10n/et_EE.php new file mode 100644 index 0000000000..ee92f9fe21 --- /dev/null +++ b/apps/admin_migrate/l10n/et_EE.php @@ -0,0 +1,5 @@ + "Ekspordi see ownCloudi paigaldus", +"This will create a compressed file that contains the data of this owncloud instance.\n Please choose the export type:" => "See loob pakitud faili, milles on sinu owncloudi paigalduse andmed.\n Palun vali eksporditava faili tüüp:", +"Export" => "Ekspordi" +); diff --git a/apps/admin_migrate/l10n/fi_FI.php b/apps/admin_migrate/l10n/fi_FI.php new file mode 100644 index 0000000000..2484eab7d3 --- /dev/null +++ b/apps/admin_migrate/l10n/fi_FI.php @@ -0,0 +1,4 @@ + "Vie tämä ownCloud-istanssi", +"Export" => "Vie" +); diff --git a/apps/admin_migrate/l10n/fr.php b/apps/admin_migrate/l10n/fr.php new file mode 100644 index 0000000000..0cf0a444b0 --- /dev/null +++ b/apps/admin_migrate/l10n/fr.php @@ -0,0 +1,5 @@ + "Exporter cette instance ownCloud", +"This will create a compressed file that contains the data of this owncloud instance.\n Please choose the export type:" => "Ceci va créer une archive compressée contenant les données de cette instance ownCloud.\n Veuillez choisir le type d'export :", +"Export" => "Exporter" +); diff --git a/apps/admin_migrate/l10n/gl.php b/apps/admin_migrate/l10n/gl.php new file mode 100644 index 0000000000..9d18e13493 --- /dev/null +++ b/apps/admin_migrate/l10n/gl.php @@ -0,0 +1,5 @@ + "Exporta esta instancia de ownCloud", +"This will create a compressed file that contains the data of this owncloud instance.\n Please choose the export type:" => "Esto creará un ficheiro comprimido que contén os datos de esta instancia de ownCloud.\nPor favor escolla o modo de exportación:", +"Export" => "Exportar" +); diff --git a/apps/admin_migrate/l10n/it.php b/apps/admin_migrate/l10n/it.php new file mode 100644 index 0000000000..94ba191ba6 --- /dev/null +++ b/apps/admin_migrate/l10n/it.php @@ -0,0 +1,5 @@ + "Esporta questa istanza di ownCloud", +"This will create a compressed file that contains the data of this owncloud instance.\n Please choose the export type:" => "Questa operazione creerà un file compresso che contiene i dati dell'istanza di ownCloud. Scegli il tipo di esportazione:", +"Export" => "Esporta" +); diff --git a/apps/admin_migrate/l10n/ja_JP.php b/apps/admin_migrate/l10n/ja_JP.php new file mode 100644 index 0000000000..f8e5944a6a --- /dev/null +++ b/apps/admin_migrate/l10n/ja_JP.php @@ -0,0 +1,5 @@ + "ownCloudをエクスポート", +"This will create a compressed file that contains the data of this owncloud instance.\n Please choose the export type:" => "このownCloudのデータを含む圧縮ファイルを生成します。\nエクスポートの種類を選択してください:", +"Export" => "エクスポート" +); diff --git a/apps/admin_migrate/l10n/lt_LT.php b/apps/admin_migrate/l10n/lt_LT.php new file mode 100644 index 0000000000..f78263da75 --- /dev/null +++ b/apps/admin_migrate/l10n/lt_LT.php @@ -0,0 +1,5 @@ + "Eksportuoti šią ownCloud instaliaciją", +"This will create a compressed file that contains the data of this owncloud instance.\n Please choose the export type:" => "Bus sukurtas archyvas su visais owncloud duomenimis ir failais.\n Pasirinkite eksportavimo tipą:", +"Export" => "Eksportuoti" +); diff --git a/apps/admin_migrate/l10n/nb_NO.php b/apps/admin_migrate/l10n/nb_NO.php new file mode 100644 index 0000000000..31f4c030bd --- /dev/null +++ b/apps/admin_migrate/l10n/nb_NO.php @@ -0,0 +1,5 @@ + "Eksporter denne ownCloud forekomsten", +"This will create a compressed file that contains the data of this owncloud instance.\n Please choose the export type:" => "Dette vil opprette en komprimert fil som inneholder dataene fra denne ownCloud forekomsten.⏎ Vennligst velg eksporttype:", +"Export" => "Eksport" +); diff --git a/apps/admin_migrate/l10n/pl.php b/apps/admin_migrate/l10n/pl.php new file mode 100644 index 0000000000..292601daa2 --- /dev/null +++ b/apps/admin_migrate/l10n/pl.php @@ -0,0 +1,5 @@ + "Eksportuj instancję ownCloud", +"This will create a compressed file that contains the data of this owncloud instance.\n Please choose the export type:" => "Spowoduje to utworzenie pliku skompresowanego, który zawiera dane tej instancji ownCloud.⏎ proszę wybrać typ eksportu:", +"Export" => "Eksport" +); diff --git a/apps/admin_migrate/l10n/sl.php b/apps/admin_migrate/l10n/sl.php new file mode 100644 index 0000000000..b8d1118bfe --- /dev/null +++ b/apps/admin_migrate/l10n/sl.php @@ -0,0 +1,5 @@ + "Izvozi to ownCloud namestitev", +"This will create a compressed file that contains the data of this owncloud instance.\n Please choose the export type:" => "Ustvarjena bo stisnjena datoteka s podatki te ownCloud namestitve.\n Prosimo, če izberete vrsto izvoza:", +"Export" => "Izvozi" +); diff --git a/apps/admin_migrate/l10n/sv.php b/apps/admin_migrate/l10n/sv.php new file mode 100644 index 0000000000..57866e897e --- /dev/null +++ b/apps/admin_migrate/l10n/sv.php @@ -0,0 +1,5 @@ + "Exportera denna instans av ownCloud", +"This will create a compressed file that contains the data of this owncloud instance.\n Please choose the export type:" => "Detta kommer att skapa en komprimerad fil som innehåller all data från denna instans av ownCloud.\n Välj exporttyp:", +"Export" => "Exportera" +); diff --git a/apps/admin_migrate/l10n/th_TH.php b/apps/admin_migrate/l10n/th_TH.php new file mode 100644 index 0000000000..9dfaca15b5 --- /dev/null +++ b/apps/admin_migrate/l10n/th_TH.php @@ -0,0 +1,5 @@ + "ส่งออกข้อมูลค่าสมมุติของ ownCloud นี้", +"This will create a compressed file that contains the data of this owncloud instance.\n Please choose the export type:" => "ส่วนนี้จะเป็นการสร้างไฟล์บีบอัดที่บรรจุข้อมูลค่าสมมุติของ ownCloud.\n กรุณาเลือกชนิดของการส่งออกข้อมูล:", +"Export" => "ส่งออก" +); diff --git a/apps/bookmarks/ajax/addBookmark.php b/apps/bookmarks/ajax/addBookmark.php index b4d0f33d72..baf3a288c1 100644 --- a/apps/bookmarks/ajax/addBookmark.php +++ b/apps/bookmarks/ajax/addBookmark.php @@ -28,9 +28,10 @@ $RUNTIME_NOSETUPFS=true; // Check if we are a user OCP\JSON::checkLoggedIn(); -OCP\JSON::checkAppEnabled('bookmarks'); OCP\JSON::callCheck(); -require_once(OC::$APPSROOT . '/apps/bookmarks/bookmarksHelper.php'); +OCP\JSON::checkAppEnabled('bookmarks'); + +require_once(OC_App::getAppPath('bookmarks').'/bookmarksHelper.php'); $id = addBookmark($_POST['url'], $_POST['title'], $_POST['tags']); -OCP\JSON::success(array('data' => $id)); \ No newline at end of file +OCP\JSON::success(array('data' => $id)); diff --git a/apps/bookmarks/ajax/delBookmark.php b/apps/bookmarks/ajax/delBookmark.php index 140da2a37d..26437ea0c8 100644 --- a/apps/bookmarks/ajax/delBookmark.php +++ b/apps/bookmarks/ajax/delBookmark.php @@ -21,13 +21,10 @@ * */ -//no apps or filesystem -$RUNTIME_NOSETUPFS=true; - - - // Check if we are a user OCP\JSON::checkLoggedIn(); +OCP\JSON::callCheck(); + OCP\JSON::checkAppEnabled('bookmarks'); OCP\JSON::callCheck(); diff --git a/apps/bookmarks/ajax/editBookmark.php b/apps/bookmarks/ajax/editBookmark.php index 8c1b19cf0c..617021e412 100644 --- a/apps/bookmarks/ajax/editBookmark.php +++ b/apps/bookmarks/ajax/editBookmark.php @@ -21,16 +21,12 @@ * */ -//no apps or filesystem -$RUNTIME_NOSETUPFS=true; - - - // Check if we are a user OCP\JSON::checkLoggedIn(); -OCP\JSON::checkAppEnabled('bookmarks'); OCP\JSON::callCheck(); +OCP\JSON::checkAppEnabled('bookmarks'); + $CONFIG_DBTYPE = OCP\Config::getSystemValue( "dbtype", "sqlite" ); if( $CONFIG_DBTYPE == 'sqlite' or $CONFIG_DBTYPE == 'sqlite3' ){ $_ut = "strftime('%s','now')"; diff --git a/apps/bookmarks/ajax/recordClick.php b/apps/bookmarks/ajax/recordClick.php index 332d58262e..785056dc11 100644 --- a/apps/bookmarks/ajax/recordClick.php +++ b/apps/bookmarks/ajax/recordClick.php @@ -21,11 +21,6 @@ * */ -//no apps or filesystem -$RUNTIME_NOSETUPFS=true; - - - // Check if we are a user OCP\JSON::checkLoggedIn(); OCP\JSON::checkAppEnabled('bookmarks'); diff --git a/apps/bookmarks/ajax/updateList.php b/apps/bookmarks/ajax/updateList.php index 4de2475d06..cf9a2cf918 100644 --- a/apps/bookmarks/ajax/updateList.php +++ b/apps/bookmarks/ajax/updateList.php @@ -22,11 +22,6 @@ * */ -//no apps or filesystem -$RUNTIME_NOSETUPFS=true; - - - // Check if we are a user OCP\JSON::checkLoggedIn(); OCP\JSON::checkAppEnabled('bookmarks'); diff --git a/apps/bookmarks/appinfo/app.php b/apps/bookmarks/appinfo/app.php index 8a8f443891..f4bca9df70 100644 --- a/apps/bookmarks/appinfo/app.php +++ b/apps/bookmarks/appinfo/app.php @@ -10,8 +10,6 @@ OC::$CLASSPATH['OC_Bookmarks_Bookmarks'] = 'apps/bookmarks/lib/bookmarks.php'; OC::$CLASSPATH['OC_Search_Provider_Bookmarks'] = 'apps/bookmarks/lib/search.php'; -OCP\App::register( array( 'order' => 70, 'id' => 'bookmark', 'name' => 'Bookmarks' )); - $l = new OC_l10n('bookmarks'); OCP\App::addNavigationEntry( array( 'id' => 'bookmarks_index', 'order' => 70, 'href' => OCP\Util::linkTo( 'bookmarks', 'index.php' ), 'icon' => OCP\Util::imagePath( 'bookmarks', 'bookmarks.png' ), 'name' => $l->t('Bookmarks'))); diff --git a/apps/bookmarks/bookmarksHelper.php b/apps/bookmarks/bookmarksHelper.php index cb0ca06c72..988042fc0e 100644 --- a/apps/bookmarks/bookmarksHelper.php +++ b/apps/bookmarks/bookmarksHelper.php @@ -90,7 +90,8 @@ function addBookmark($url, $title, $tags='') { if(empty($title)) { $metadata = getURLMetadata($url); - $title = $metadata['title']; + if(isset($metadata['title'])) // Check for problems fetching the title + $title = $metadata['title']; } if(empty($title)) { diff --git a/apps/bookmarks/img/bookmarks.png b/apps/bookmarks/img/bookmarks.png index b92e4f50a4..3e8eed2380 100644 Binary files a/apps/bookmarks/img/bookmarks.png and b/apps/bookmarks/img/bookmarks.png differ diff --git a/apps/bookmarks/l10n/bg_BG.php b/apps/bookmarks/l10n/bg_BG.php new file mode 100644 index 0000000000..04d731b107 --- /dev/null +++ b/apps/bookmarks/l10n/bg_BG.php @@ -0,0 +1,12 @@ + "Отметки", +"unnamed" => "неозаглавено", +"Drag this to your browser bookmarks and click it, when you want to bookmark a webpage quickly:" => "Завлачете това в лентата с отметки на браузъра си и го натискайте, когато искате да отметнете бързо някоя страница:", +"Read later" => "Отмятане", +"Address" => "Адрес", +"Title" => "Заглавие", +"Tags" => "Етикети", +"Save bookmark" => "Запис на отметката", +"You have no bookmarks" => "Нямате отметки", +"Bookmarklet
    " => "Бутон за отметки
    " +); diff --git a/apps/bookmarks/l10n/ca.php b/apps/bookmarks/l10n/ca.php new file mode 100644 index 0000000000..cf90d9a887 --- /dev/null +++ b/apps/bookmarks/l10n/ca.php @@ -0,0 +1,12 @@ + "Adreces d'interès", +"unnamed" => "sense nom", +"Drag this to your browser bookmarks and click it, when you want to bookmark a webpage quickly:" => "Arrossegueu-ho al navegador i feu-hi un clic quan volgueu marcar ràpidament una adreça d'interès:", +"Read later" => "Llegeix més tard", +"Address" => "Adreça", +"Title" => "Títol", +"Tags" => "Etiquetes", +"Save bookmark" => "Desa l'adreça d'interès", +"You have no bookmarks" => "No teniu adreces d'interès", +"Bookmarklet
    " => "Bookmarklet
    " +); diff --git a/apps/bookmarks/l10n/cs_CZ.php b/apps/bookmarks/l10n/cs_CZ.php new file mode 100644 index 0000000000..2251969a26 --- /dev/null +++ b/apps/bookmarks/l10n/cs_CZ.php @@ -0,0 +1,12 @@ + "Záložky", +"unnamed" => "nepojmenovaný", +"Drag this to your browser bookmarks and click it, when you want to bookmark a webpage quickly:" => "Přetáhněte do Vašeho prohlížeče a kliněte, pokud si přejete rychle uložit stranu do záložek:", +"Read later" => "Přečíst později", +"Address" => "Adresa", +"Title" => "Název", +"Tags" => "Tagy", +"Save bookmark" => "Uložit záložku", +"You have no bookmarks" => "Nemáte žádné záložky", +"Bookmarklet
    " => "Záložky
    " +); diff --git a/apps/bookmarks/l10n/de.php b/apps/bookmarks/l10n/de.php new file mode 100644 index 0000000000..14a54f1cce --- /dev/null +++ b/apps/bookmarks/l10n/de.php @@ -0,0 +1,12 @@ + "Lesezeichen", +"unnamed" => "unbenannt", +"Drag this to your browser bookmarks and click it, when you want to bookmark a webpage quickly:" => "Ziehen Sie dies zu Ihren Browser-Lesezeichen und klicken Sie darauf, wenn Sie eine Website schnell den Lesezeichen hinzufügen wollen.", +"Read later" => "Später lesen", +"Address" => "Adresse", +"Title" => "Titel", +"Tags" => "Tags", +"Save bookmark" => "Lesezeichen speichern", +"You have no bookmarks" => "Sie haben keine Lesezeichen", +"Bookmarklet
    " => "Bookmarklet
    " +); diff --git a/apps/bookmarks/l10n/el.php b/apps/bookmarks/l10n/el.php new file mode 100644 index 0000000000..f282a1bbf8 --- /dev/null +++ b/apps/bookmarks/l10n/el.php @@ -0,0 +1,12 @@ + "Σελιδοδείκτες", +"unnamed" => "ανώνυμο", +"Drag this to your browser bookmarks and click it, when you want to bookmark a webpage quickly:" => "Σύρετε αυτό στους σελιδοδείκτες του περιηγητή σας και κάντε κλικ επάνω του, όταν θέλετε να προσθέσετε σύντομα μια ιστοσελίδα ως σελιδοδείκτη:", +"Read later" => "Ανάγνωση αργότερα", +"Address" => "Διεύθυνση", +"Title" => "Τίτλος", +"Tags" => "Ετικέτες", +"Save bookmark" => "Αποθήκευση σελιδοδείκτη", +"You have no bookmarks" => "Δεν έχετε σελιδοδείκτες", +"Bookmarklet
    " => "Εφαρμογίδιο Σελιδοδεικτών
    " +); diff --git a/apps/bookmarks/l10n/eo.php b/apps/bookmarks/l10n/eo.php new file mode 100644 index 0000000000..808cda8a04 --- /dev/null +++ b/apps/bookmarks/l10n/eo.php @@ -0,0 +1,11 @@ + "Legosignoj", +"unnamed" => "nenomita", +"Drag this to your browser bookmarks and click it, when you want to bookmark a webpage quickly:" => "Ŝovu tion ĉi al la legosignoj de via TTT-legilo kaj klaku ĝin, se vi volas rapide legosignigi TTT-paĝon:", +"Read later" => "Legi poste", +"Address" => "Adreso", +"Title" => "Titolo", +"Tags" => "Etikedoj", +"Save bookmark" => "Konservi legosignon", +"You have no bookmarks" => "Vi havas neniun legosignon" +); diff --git a/apps/bookmarks/l10n/es.php b/apps/bookmarks/l10n/es.php new file mode 100644 index 0000000000..071b0dda70 --- /dev/null +++ b/apps/bookmarks/l10n/es.php @@ -0,0 +1,12 @@ + "Marcadores", +"unnamed" => "sin nombre", +"Drag this to your browser bookmarks and click it, when you want to bookmark a webpage quickly:" => "Arrastra desde aquí a los marcadores de tu navegador, y haz clic cuando quieras marcar una página web rápidamente:", +"Read later" => "Leer después", +"Address" => "Dirección", +"Title" => "Título", +"Tags" => "Etiquetas", +"Save bookmark" => "Guardar marcador", +"You have no bookmarks" => "No tienes marcadores", +"Bookmarklet
    " => "Bookmarklet
    " +); diff --git a/apps/bookmarks/l10n/et_EE.php b/apps/bookmarks/l10n/et_EE.php new file mode 100644 index 0000000000..da9e4d92a6 --- /dev/null +++ b/apps/bookmarks/l10n/et_EE.php @@ -0,0 +1,10 @@ + "Järjehoidjad", +"unnamed" => "nimetu", +"Read later" => "Loe hiljem", +"Address" => "Aadress", +"Title" => "Pealkiri", +"Tags" => "Sildid", +"Save bookmark" => "Salvesta järjehoidja", +"You have no bookmarks" => "Sul pole järjehoidjaid" +); diff --git a/apps/bookmarks/l10n/fa.php b/apps/bookmarks/l10n/fa.php new file mode 100644 index 0000000000..b46ce911d4 --- /dev/null +++ b/apps/bookmarks/l10n/fa.php @@ -0,0 +1,8 @@ + "نشانک‌ها", +"unnamed" => "بدون‌نام", +"Address" => "آدرس", +"Title" => "عنوان", +"Save bookmark" => "ذخیره نشانک", +"You have no bookmarks" => "شما هیچ نشانکی ندارید" +); diff --git a/apps/bookmarks/l10n/fi_FI.php b/apps/bookmarks/l10n/fi_FI.php new file mode 100644 index 0000000000..c814747411 --- /dev/null +++ b/apps/bookmarks/l10n/fi_FI.php @@ -0,0 +1,12 @@ + "Kirjanmerkit", +"unnamed" => "nimetön", +"Drag this to your browser bookmarks and click it, when you want to bookmark a webpage quickly:" => "Vedä tämä selaimesi kirjanmerkkipalkkiin ja napsauta sitä, kun haluat lisätä kirjanmerkin nopeasti:", +"Read later" => "Lue myöhemmin", +"Address" => "Osoite", +"Title" => "Otsikko", +"Tags" => "Tunnisteet", +"Save bookmark" => "Tallenna kirjanmerkki", +"You have no bookmarks" => "Sinulla ei ole kirjanmerkkejä", +"Bookmarklet
    " => "Kirjanmerkitsin
    " +); diff --git a/apps/bookmarks/l10n/fr.php b/apps/bookmarks/l10n/fr.php new file mode 100644 index 0000000000..508c82369f --- /dev/null +++ b/apps/bookmarks/l10n/fr.php @@ -0,0 +1,12 @@ + "Favoris", +"unnamed" => "sans titre", +"Drag this to your browser bookmarks and click it, when you want to bookmark a webpage quickly:" => "Glissez ceci dans les favoris de votre navigateur, et cliquer dessus lorsque vous souhaitez ajouter la page en cours à vos marques-pages :", +"Read later" => "Lire plus tard", +"Address" => "Adresse", +"Title" => "Titre", +"Tags" => "Étiquettes", +"Save bookmark" => "Sauvegarder le favori", +"You have no bookmarks" => "Vous n'avez aucun favori", +"Bookmarklet
    " => "Gestionnaire de favoris
    " +); diff --git a/apps/bookmarks/l10n/it.php b/apps/bookmarks/l10n/it.php new file mode 100644 index 0000000000..862d75bde4 --- /dev/null +++ b/apps/bookmarks/l10n/it.php @@ -0,0 +1,12 @@ + "Segnalibri", +"unnamed" => "senza nome", +"Drag this to your browser bookmarks and click it, when you want to bookmark a webpage quickly:" => "Quando vuoi creare rapidamente un segnalibro, trascinalo sui segnalibri del browser e fai clic su di esso:", +"Read later" => "Leggi dopo", +"Address" => "Indirizzo", +"Title" => "Titolo", +"Tags" => "Tag", +"Save bookmark" => "Salva segnalibro", +"You have no bookmarks" => "Non hai segnalibri", +"Bookmarklet
    " => "Bookmarklet
    " +); diff --git a/apps/bookmarks/l10n/lt_LT.php b/apps/bookmarks/l10n/lt_LT.php new file mode 100644 index 0000000000..58faddc233 --- /dev/null +++ b/apps/bookmarks/l10n/lt_LT.php @@ -0,0 +1,3 @@ + "be pavadinimo" +); diff --git a/apps/bookmarks/l10n/nb_NO.php b/apps/bookmarks/l10n/nb_NO.php new file mode 100644 index 0000000000..12e63887d2 --- /dev/null +++ b/apps/bookmarks/l10n/nb_NO.php @@ -0,0 +1,11 @@ + "Bokmerker", +"unnamed" => "uten navn", +"Drag this to your browser bookmarks and click it, when you want to bookmark a webpage quickly:" => "Dra denne over din nettlesers bokmerker og klikk den, hvis du ønsker å hurtig legge til bokmerke for en nettside", +"Read later" => "Les senere", +"Address" => "Adresse", +"Title" => "Tittel", +"Tags" => "Etikett", +"Save bookmark" => "Lagre bokmerke", +"You have no bookmarks" => "Du har ingen bokmerker" +); diff --git a/apps/bookmarks/l10n/sl.php b/apps/bookmarks/l10n/sl.php new file mode 100644 index 0000000000..32a4162908 --- /dev/null +++ b/apps/bookmarks/l10n/sl.php @@ -0,0 +1,12 @@ + "Zaznamki", +"unnamed" => "neimenovano", +"Drag this to your browser bookmarks and click it, when you want to bookmark a webpage quickly:" => "Povlecite to povezavo med zaznamke v vašem brskalniku in jo, ko želite ustvariti zaznamek trenutne strani, preprosto kliknite:", +"Read later" => "Preberi kasneje", +"Address" => "Naslov", +"Title" => "Ime", +"Tags" => "Oznake", +"Save bookmark" => "Shrani zaznamek", +"You have no bookmarks" => "Nimate zaznamkov", +"Bookmarklet
    " => "Bookmarklet
    " +); diff --git a/apps/bookmarks/l10n/sv.php b/apps/bookmarks/l10n/sv.php new file mode 100644 index 0000000000..689bd452f1 --- /dev/null +++ b/apps/bookmarks/l10n/sv.php @@ -0,0 +1,12 @@ + "Bokmärken", +"unnamed" => "namnlös", +"Drag this to your browser bookmarks and click it, when you want to bookmark a webpage quickly:" => "Dra till din webbläsares bokmärken och klicka på det när du vill bokmärka en webbsida snabbt:", +"Read later" => "Läs senare", +"Address" => "Adress", +"Title" => "Titel", +"Tags" => "Taggar", +"Save bookmark" => "Spara bokmärke", +"You have no bookmarks" => "Du har inga bokmärken", +"Bookmarklet
    " => "Skriptbokmärke
    " +); diff --git a/apps/bookmarks/l10n/th_TH.php b/apps/bookmarks/l10n/th_TH.php new file mode 100644 index 0000000000..64a148efab --- /dev/null +++ b/apps/bookmarks/l10n/th_TH.php @@ -0,0 +1,12 @@ + "รายการโปรด", +"unnamed" => "ยังไม่มีชื่อ", +"Drag this to your browser bookmarks and click it, when you want to bookmark a webpage quickly:" => "ลากสิ่งนี้ไปไว้ที่รายการโปรดในโปรแกรมบราวเซอร์ของคุณ แล้วคลิกที่นั่น, เมื่อคุณต้องการเก็บหน้าเว็บเพจเข้าไปไว้ในรายการโปรดอย่างรวดเร็ว", +"Read later" => "อ่านภายหลัง", +"Address" => "ที่อยู่", +"Title" => "ชื่อ", +"Tags" => "ป้ายกำกับ", +"Save bookmark" => "บันทึกรายการโปรด", +"You have no bookmarks" => "คุณยังไม่มีรายการโปรด", +"Bookmarklet
    " => "Bookmarklet
    " +); diff --git a/apps/bookmarks/lib/bookmarks.php b/apps/bookmarks/lib/bookmarks.php index d569bf528a..e1e1338890 100644 --- a/apps/bookmarks/lib/bookmarks.php +++ b/apps/bookmarks/lib/bookmarks.php @@ -145,4 +145,3 @@ class OC_Bookmarks_Bookmarks{ return true; } } -?> diff --git a/apps/bookmarks/templates/addBm.php b/apps/bookmarks/templates/addBm.php index dbe673f53a..357e0a18f2 100644 --- a/apps/bookmarks/templates/addBm.php +++ b/apps/bookmarks/templates/addBm.php @@ -3,9 +3,9 @@ Read later - ownCloud -

    Saved!

    + Close the window \ No newline at end of file diff --git a/apps/bookmarks/templates/list.php b/apps/bookmarks/templates/list.php index fdd2b19f79..4b84b43890 100644 --- a/apps/bookmarks/templates/list.php +++ b/apps/bookmarks/templates/list.php @@ -7,7 +7,7 @@ * See the COPYING-README file. */ ?> - +
    @@ -20,7 +20,7 @@ diff --git a/apps/calendar/ajax/cache/rescan.php b/apps/calendar/ajax/cache/rescan.php new file mode 100644 index 0000000000..3417f1ae4b --- /dev/null +++ b/apps/calendar/ajax/cache/rescan.php @@ -0,0 +1,15 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ +OCP\JSON::checkLoggedIn(); +OCP\JSON::checkAppEnabled('calendar'); +$calendars = OC_Calendar_Calendar::allCalendars(OCP\USER::getUser()); +foreach($calendars as $calendar){ + OC_Calendar_Repeat::cleancalendar($calendar['id']); + OC_Calendar_Repeat::generatecalendar($calendar['id']); +} +OCP\JSON::success(); \ No newline at end of file diff --git a/apps/calendar/ajax/cache/status.php b/apps/calendar/ajax/cache/status.php new file mode 100644 index 0000000000..d2806d4789 --- /dev/null +++ b/apps/calendar/ajax/cache/status.php @@ -0,0 +1,22 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ +OCP\JSON::checkLoggedIn(); +OCP\JSON::checkAppEnabled('calendar'); +$calendars = OC_Calendar_Calendar::allCalendars(OCP\USER::getUser()); +$allcached = true; +foreach($calendars as $calendar){ + if(!OC_Calendar_Repeat::is_calendar_cached($calendar['id'])){ + $allcached = false; + } +} +$l = new OC_L10N('calendar'); +if(!$allcached){ + OCP\JSON::error(array('message'=>'Not all calendars are completely cached', 'l10n'=>$l->t('Not all calendars are completely cached'))); +}else{ + OCP\JSON::success(array('message'=>'Everything seems to be completely cached', 'l10n'=>$l->t('Everything seems to be completely cached'))); +} \ No newline at end of file diff --git a/apps/calendar/ajax/calendar/activation.php b/apps/calendar/ajax/calendar/activation.php index e31908beb1..f4aadc5b01 100644 --- a/apps/calendar/ajax/calendar/activation.php +++ b/apps/calendar/ajax/calendar/activation.php @@ -9,6 +9,8 @@ OCP\JSON::checkLoggedIn(); OCP\JSON::checkAppEnabled('calendar'); +OCP\JSON::callCheck(); + $calendarid = $_POST['calendarid']; $calendar = OC_Calendar_App::getCalendar($calendarid, true); if(!$calendar){ diff --git a/apps/calendar/ajax/calendar/edit.form.php b/apps/calendar/ajax/calendar/edit.form.php index ae056a524b..3916c52763 100644 --- a/apps/calendar/ajax/calendar/edit.form.php +++ b/apps/calendar/ajax/calendar/edit.form.php @@ -11,9 +11,9 @@ OCP\JSON::checkLoggedIn(); OCP\JSON::checkAppEnabled('calendar'); $calendarcolor_options = OC_Calendar_Calendar::getCalendarColorOptions(); -$calendar = OC_Calendar_App::getCalendar($_GET['calendarid']); +$calendar = OC_Calendar_App::getCalendar($_POST['calendarid']); $tmpl = new OCP\Template("calendar", "part.editcalendar"); $tmpl->assign('new', false); $tmpl->assign('calendarcolor_options', $calendarcolor_options); $tmpl->assign('calendar', $calendar); -$tmpl->printPage(); +$tmpl->printPage(); \ No newline at end of file diff --git a/apps/calendar/ajax/calendar/new.php b/apps/calendar/ajax/calendar/new.php index 34b056abe8..67d1282237 100644 --- a/apps/calendar/ajax/calendar/new.php +++ b/apps/calendar/ajax/calendar/new.php @@ -6,8 +6,6 @@ * See the COPYING-README file. */ - - // Check if we are a user OCP\JSON::checkLoggedIn(); OCP\JSON::checkAppEnabled('calendar'); @@ -32,7 +30,13 @@ OC_Calendar_Calendar::setCalendarActive($calendarid, 1); $calendar = OC_Calendar_Calendar::find($calendarid); $tmpl = new OCP\Template('calendar', 'part.choosecalendar.rowfields'); $tmpl->assign('calendar', $calendar); +if(OC_Calendar_Share::allUsersSharedwith($calendarid, OC_Calendar_Share::CALENDAR) == array()){ + $shared = false; +}else{ + $shared = true; +} +$tmpl->assign('shared', $shared); OCP\JSON::success(array( 'page' => $tmpl->fetchPage(), 'eventSource' => OC_Calendar_Calendar::getEventSourceInfo($calendar), -)); \ No newline at end of file +)); diff --git a/apps/calendar/ajax/calendar/overview.php b/apps/calendar/ajax/calendar/overview.php index 9d43364ffb..1d8e49ea5f 100644 --- a/apps/calendar/ajax/calendar/overview.php +++ b/apps/calendar/ajax/calendar/overview.php @@ -4,9 +4,7 @@ * This file is licensed under the Affero General Public License version 3 or * later. * See the COPYING-README file. - */ - - + */ $l10n = OC_L10N::get('calendar'); OCP\JSON::checkLoggedIn(); OCP\JSON::checkAppEnabled('calendar'); diff --git a/apps/calendar/ajax/calendar/update.php b/apps/calendar/ajax/calendar/update.php index 740094775f..c09b1008c9 100644 --- a/apps/calendar/ajax/calendar/update.php +++ b/apps/calendar/ajax/calendar/update.php @@ -37,7 +37,13 @@ OC_Calendar_Calendar::setCalendarActive($calendarid, $_POST['active']); $calendar = OC_Calendar_App::getCalendar($calendarid); $tmpl = new OCP\Template('calendar', 'part.choosecalendar.rowfields'); $tmpl->assign('calendar', $calendar); +if(OC_Calendar_Share::allUsersSharedwith($calendarid, OC_Calendar_Share::CALENDAR) == array()){ + $shared = false; +}else{ + $shared = true; +} +$tmpl->assign('shared', $shared); OCP\JSON::success(array( 'page' => $tmpl->fetchPage(), 'eventSource' => OC_Calendar_Calendar::getEventSourceInfo($calendar), -)); \ No newline at end of file +)); diff --git a/apps/calendar/ajax/categories/rescan.php b/apps/calendar/ajax/categories/rescan.php index f0060cb23b..08c32865b6 100644 --- a/apps/calendar/ajax/categories/rescan.php +++ b/apps/calendar/ajax/categories/rescan.php @@ -9,6 +9,7 @@ OCP\JSON::checkLoggedIn(); OCP\JSON::checkAppEnabled('calendar'); +OCP\JSON::callCheck(); foreach ($_POST as $key=>$element) { debug('_POST: '.$key.'=>'.print_r($element, true)); diff --git a/apps/calendar/ajax/changeview.php b/apps/calendar/ajax/changeview.php index 951f603ce8..819025543a 100644 --- a/apps/calendar/ajax/changeview.php +++ b/apps/calendar/ajax/changeview.php @@ -7,15 +7,15 @@ */ OCP\JSON::checkLoggedIn(); OCP\JSON::checkAppEnabled('calendar'); -$view = $_GET['v']; +$view = $_POST['v']; switch($view){ case 'agendaWeek': case 'month'; case 'list': break; default: - OCP\JSON::error(array('message'=>'unexspected parameter: ' . $view)); + OCP\JSON::error(array('message'=>'unexpected parameter: ' . $view)); exit; } OCP\Config::setUserValue(OCP\USER::getUser(), 'calendar', 'currentview', $view); -OCP\JSON::success(); +OCP\JSON::success(); \ No newline at end of file diff --git a/apps/calendar/ajax/event/edit.form.php b/apps/calendar/ajax/event/edit.form.php index e5cf573c71..2751248153 100644 --- a/apps/calendar/ajax/event/edit.form.php +++ b/apps/calendar/ajax/event/edit.form.php @@ -13,7 +13,7 @@ if(!OCP\User::isLoggedIn()) { } OCP\JSON::checkAppEnabled('calendar'); -$id = $_GET['id']; +$id = $_POST['id']; $data = OC_Calendar_App::getEventObject($id, true, true); if(!$data){ diff --git a/apps/calendar/ajax/event/new.form.php b/apps/calendar/ajax/event/new.form.php index 0b19e7e92f..db04cdf2d4 100644 --- a/apps/calendar/ajax/event/new.form.php +++ b/apps/calendar/ajax/event/new.form.php @@ -27,7 +27,7 @@ if (!$end){ } $start = new DateTime('@'.$start); $end = new DateTime('@'.$end); -$timezone = OCP\Config::getUserValue(OCP\USER::getUser(), 'calendar', 'timezone', date_default_timezone_get()); +$timezone = OC_Calendar_App::getTimezone(); $start->setTimezone(new DateTimeZone($timezone)); $end->setTimezone(new DateTimeZone($timezone)); diff --git a/apps/calendar/ajax/event/resize.php b/apps/calendar/ajax/event/resize.php index 56b83205e8..15b687b55d 100644 --- a/apps/calendar/ajax/event/resize.php +++ b/apps/calendar/ajax/event/resize.php @@ -7,6 +7,7 @@ */ OCP\JSON::checkLoggedIn(); +OCP\JSON::callCheck(); $id = $_POST['id']; diff --git a/apps/calendar/ajax/events.php b/apps/calendar/ajax/events.php index e00e0cfeb1..ae55cbc02d 100644 --- a/apps/calendar/ajax/events.php +++ b/apps/calendar/ajax/events.php @@ -5,25 +5,20 @@ * later. * See the COPYING-README file. */ - - -require_once('when/When.php'); - OCP\JSON::checkLoggedIn(); OCP\JSON::checkAppEnabled('calendar'); session_write_close(); // Look for the calendar id -$calendar_id = OC_Calendar_App::getCalendar($_GET['calendar_id'], false, false); -if($calendar_id !== false){ - if(! is_numeric($calendar_id['userid']) && $calendar_id['userid'] != OCP\User::getUser()){ - OCP\JSON::error(); - exit; +$calendar_id = null; +if (strval(intval($_GET['calendar_id'])) == strval($_GET['calendar_id'])) { // integer for sure. + $id = intval($_GET['calendar_id']); + $calendarrow = OC_Calendar_App::getCalendar($id, true, false); // Let's at least security check otherwise we might as well use OC_Calendar_Calendar::find() + if($calendarrow !== false && is_int($calendar_id['userid']) && $id == $calendar_id['userid']) { + $calendar_id = $id; } } -else { - $calendar_id = $_GET['calendar_id']; -} +$calendar_id = (is_null($calendar_id)?strip_tags($_GET['calendar_id']):$calendar_id); $start = (version_compare(PHP_VERSION, '5.3.0', '>='))?DateTime::createFromFormat('U', $_GET['start']):new DateTime('@' . $_GET['start']); $end = (version_compare(PHP_VERSION, '5.3.0', '>='))?DateTime::createFromFormat('U', $_GET['end']):new DateTime('@' . $_GET['end']); diff --git a/apps/calendar/ajax/import/calendarcheck.php b/apps/calendar/ajax/import/calendarcheck.php new file mode 100644 index 0000000000..a91bab7057 --- /dev/null +++ b/apps/calendar/ajax/import/calendarcheck.php @@ -0,0 +1,18 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ +OCP\JSON::checkLoggedIn(); +OCP\App::checkAppEnabled('calendar'); +$calname = strip_tags($_POST['calname']); +$calendars = OC_Calendar_Calendar::allCalendars(OCP\User::getUser()); +foreach($calendars as $calendar){ + if($calendar['displayname'] == $calname){ + OCP\JSON::success(array('message'=>'exists')); + exit; + } +} +OCP\JSON::error(); \ No newline at end of file diff --git a/apps/calendar/ajax/import/dialog.php b/apps/calendar/ajax/import/dialog.php index b99c32278c..18fe226172 100644 --- a/apps/calendar/ajax/import/dialog.php +++ b/apps/calendar/ajax/import/dialog.php @@ -5,8 +5,6 @@ * later. * See the COPYING-README file. */ - - OCP\JSON::checkLoggedIn(); OCP\App::checkAppEnabled('calendar'); $tmpl = new OCP\Template('calendar', 'part.import'); diff --git a/apps/calendar/ajax/import/dropimport.php b/apps/calendar/ajax/import/dropimport.php new file mode 100644 index 0000000000..f46e731409 --- /dev/null +++ b/apps/calendar/ajax/import/dropimport.php @@ -0,0 +1,32 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ +$data = $_POST['data']; +$data = explode(',', $data); +$data = end($data); +$data = base64_decode($data); +OCP\JSON::checkLoggedIn(); +OCP\App::checkAppEnabled('calendar'); +$import = new OC_Calendar_Import($data); +$import->setUserID(OCP\User::getUser()); +$import->setTimeZone(OC_Calendar_App::$tz); +$import->disableProgressCache(); +if(!$import->isValid()){ + OCP\JSON::error(); + exit; +} +$newcalendarname = strip_tags($import->createCalendarName()); +$newid = OC_Calendar_Calendar::addCalendar(OCP\User::getUser(),$newcalendarname,'VEVENT,VTODO,VJOURNAL',null,0,$import->createCalendarColor()); +$import->setCalendarID($newid); +$import->import(); +$count = $import->getCount(); +if($count == 0){ + OC_Calendar_Calendar::deleteCalendar($newid); + OCP\JSON::error(array('message'=>OC_Calendar_App::$l10n->t('The file contained either no events or all events are already saved in your calendar.'))); +}else{ + OCP\JSON::success(array('message'=>$count . ' ' . OC_Calendar_App::$l10n->t('events has been saved in the new calendar') . ' ' . $newcalendarname, 'eventSource'=>OC_Calendar_Calendar::getEventSourceInfo(OC_Calendar_Calendar::find($newid)))); +} \ No newline at end of file diff --git a/apps/calendar/ajax/import/import.php b/apps/calendar/ajax/import/import.php index c0cd140376..b1dfc464d0 100644 --- a/apps/calendar/ajax/import/import.php +++ b/apps/calendar/ajax/import/import.php @@ -5,45 +5,77 @@ * later. * See the COPYING-README file. */ -//check for calendar rights or create new one -ob_start(); OCP\JSON::checkLoggedIn(); -OCP\JSON::callCheck(); OCP\App::checkAppEnabled('calendar'); -$nl="\r\n"; -$comps = array('VEVENT'=>true, 'VTODO'=>true, 'VJOURNAL'=>true); -$progressfile = 'import_tmp/' . md5(session_id()) . '.txt'; -if(is_writable('import_tmp/')){ - $progressfopen = fopen($progressfile, 'w'); - fwrite($progressfopen, '10'); - fclose($progressfopen); +OCP\JSON::callCheck(); +session_write_close(); +if (isset($_POST['progresskey']) && isset($_POST['getprogress'])) { + echo OCP\JSON::success(array('percent'=>OC_Cache::get($_POST['progresskey']))); + exit; } $file = OC_Filesystem::file_get_contents($_POST['path'] . '/' . $_POST['file']); +if(!$file){ + OCP\JSON::error(array('error'=>'404')); +} +$import = new OC_Calendar_Import($file); +$import->setUserID(OCP\User::getUser()); +$import->setTimeZone(OC_Calendar_App::$tz); +$import->enableProgressCache(); +$import->setProgresskey($_POST['progresskey']); +if(!$import->isValid()){ + OCP\JSON::error(array('error'=>'notvalid')); + exit; +} +$newcal = false; if($_POST['method'] == 'new'){ - $id = OC_Calendar_Calendar::addCalendar(OCP\USER::getUser(), $_POST['calname']); - OC_Calendar_Calendar::setCalendarActive($id, 1); + $calendars = OC_Calendar_Calendar::allCalendars(OCP\User::getUser()); + foreach($calendars as $calendar){ + if($calendar['displayname'] == $_POST['calname']){ + $id = $calendar['id']; + $newcal = false; + break; + } + $newcal = true; + } + if($newcal){ + $id = OC_Calendar_Calendar::addCalendar(OCP\USER::getUser(), strip_tags($_POST['calname']),'VEVENT,VTODO,VJOURNAL',null,0,strip_tags($_POST['calcolor'])); + OC_Calendar_Calendar::setCalendarActive($id, 1); + } }else{ $calendar = OC_Calendar_App::getCalendar($_POST['id']); if($calendar['userid'] != OCP\USER::getUser()){ - OCP\JSON::error(); + OCP\JSON::error(array('error'=>'missingcalendarrights')); exit(); } $id = $_POST['id']; } -if(is_writable('import_tmp/')){ - $progressfopen = fopen($progressfile, 'w'); - fwrite($progressfopen, '20'); - fclose($progressfopen); +$import->setCalendarID($id); +try{ + $import->import(); +}catch (Exception $e) { + OCP\JSON::error(array('message'=>OC_Calendar_App::$l10n->t('Import failed'), 'debug'=>$e->getMessage())); + //write some log } +$count = $import->getCount(); +if($count == 0){ + if($newcal){ + OC_Calendar_Calendar::deleteCalendar($id); + } + OCP\JSON::error(array('message'=>OC_Calendar_App::$l10n->t('The file contained either no events or all events are already saved in your calendar.'))); +}else{ + if($newcal){ + OCP\JSON::success(array('message'=>$count . ' ' . OC_Calendar_App::$l10n->t('events has been saved in the new calendar') . ' ' . strip_tags($_POST['calname']))); + }else{ + OCP\JSON::success(array('message'=>$count . ' ' . OC_Calendar_App::$l10n->t('events has been saved in your calendar'))); + } +} +/* //////////////////////////// Attention: following code is quite painfull !!! /////////////////////// +writeProgress('20'); // normalize the newlines $file = str_replace(array("\r","\n\n"), array("\n","\n"), $file); $lines = explode("\n", $file); unset($file); -if(is_writable('import_tmp/')){ - $progressfopen = fopen($progressfile, 'w'); - fwrite($progressfopen, '30'); - fclose($progressfopen); -} +writeProgress('30'); // analyze the file, group components by uid, and keep refs to originating calendar object // $cals is array calendar objects, keys are 1st line# $cal, ie array( $cal => $caldata ) // $caldata is array( 'first' => 1st component line#, 'last' => last comp line#, 'end' => end line# ) @@ -87,13 +119,8 @@ foreach($lines as $line) { $i++; } // import the calendar -if(is_writable('import_tmp/')){ - $progressfopen = fopen($progressfile, 'w'); - fwrite($progressfopen, '60'); - fclose($progressfopen); -} +writeProgress('60'); foreach($uids as $uid) { - $prefix=$suffix=$content=array(); foreach($uid as $begin=>$details) { @@ -118,13 +145,7 @@ foreach($uids as $uid) { } } // finished import -if(is_writable('import_tmp/')){ - $progressfopen = fopen($progressfile, 'w'); - fwrite($progressfopen, '100'); - fclose($progressfopen); -} +writeProgress('100'); sleep(3); -if(is_writable('import_tmp/')){ - unlink($progressfile); -} -OCP\JSON::success(); \ No newline at end of file +OC_Cache::remove($progresskey); +OCP\JSON::success();*/ diff --git a/apps/calendar/ajax/settings/guesstimezone.php b/apps/calendar/ajax/settings/guesstimezone.php index 11c74631d4..6b6b8bef9c 100644 --- a/apps/calendar/ajax/settings/guesstimezone.php +++ b/apps/calendar/ajax/settings/guesstimezone.php @@ -12,8 +12,8 @@ OCP\JSON::checkAppEnabled('calendar'); $l = OC_L10N::get('calendar'); -$lat = $_GET['lat']; -$lng = $_GET['long']; +$lat = $_POST['lat']; +$lng = $_POST['lng']; $timezone = OC_Geo::timezone($lat, $lng); @@ -23,4 +23,4 @@ if($timezone == OCP\Config::getUserValue(OCP\USER::getUser(), 'calendar', 'timez } OCP\Config::setUserValue(OCP\USER::getUser(), 'calendar', 'timezone', $timezone); $message = array('message'=> $l->t('New Timezone:') . $timezone); -OCP\JSON::success($message); +OCP\JSON::success($message); \ No newline at end of file diff --git a/apps/calendar/ajax/settings/setfirstday.php b/apps/calendar/ajax/settings/setfirstday.php index 97c2488293..73cf0c19b7 100644 --- a/apps/calendar/ajax/settings/setfirstday.php +++ b/apps/calendar/ajax/settings/setfirstday.php @@ -8,7 +8,6 @@ OCP\JSON::checkLoggedIn(); OCP\JSON::callCheck(); - if(isset($_POST["firstday"])){ OCP\Config::setUserValue(OCP\USER::getUser(), 'calendar', 'firstday', $_POST["firstday"]); OCP\JSON::success(); diff --git a/apps/calendar/ajax/settings/settimeformat.php b/apps/calendar/ajax/settings/settimeformat.php index d09679b927..6136857e2f 100644 --- a/apps/calendar/ajax/settings/settimeformat.php +++ b/apps/calendar/ajax/settings/settimeformat.php @@ -8,7 +8,6 @@ OCP\JSON::checkLoggedIn(); OCP\JSON::callCheck(); - if(isset($_POST["timeformat"])){ OCP\Config::setUserValue(OCP\USER::getUser(), 'calendar', 'timeformat', $_POST["timeformat"]); OCP\JSON::success(); diff --git a/apps/calendar/ajax/settings/settimezone.php b/apps/calendar/ajax/settings/settimezone.php index 6d029a6643..06db66d578 100644 --- a/apps/calendar/ajax/settings/settimezone.php +++ b/apps/calendar/ajax/settings/settimezone.php @@ -14,6 +14,7 @@ $l=OC_L10N::get('calendar'); // Check if we are a user OCP\JSON::checkLoggedIn(); OCP\JSON::checkAppEnabled('calendar'); +OCP\JSON::callCheck(); // Get data if( isset( $_POST['timezone'] ) ){ diff --git a/apps/calendar/ajax/share/activation.php b/apps/calendar/ajax/share/activation.php index 7d6b8fcb16..bce8693577 100644 --- a/apps/calendar/ajax/share/activation.php +++ b/apps/calendar/ajax/share/activation.php @@ -5,7 +5,7 @@ * later. * See the COPYING-README file. */ -$id = strip_tags($_GET['id']); -$activation = strip_tags($_GET['activation']); +$id = strip_tags($_POST['id']); +$activation = strip_tags($_POST['activation']); OC_Calendar_Share::set_active(OCP\USER::getUser(), $id, $activation); OCP\JSON::success(); diff --git a/apps/calendar/ajax/share/changepermission.php b/apps/calendar/ajax/share/changepermission.php index f3c628e847..5aff7666f7 100644 --- a/apps/calendar/ajax/share/changepermission.php +++ b/apps/calendar/ajax/share/changepermission.php @@ -5,11 +5,12 @@ * later. * See the COPYING-README file. */ - OCP\JSON::callCheck(); -$id = strip_tags($_GET['id']); -$idtype = strip_tags($_GET['idtype']); -$permission = (int) strip_tags($_GET['permission']); +OCP\JSON::callCheck(); + +$id = strip_tags($_POST['id']); +$idtype = strip_tags($_POST['idtype']); +$permission = (int) strip_tags($_POST['permission']); switch($idtype){ case 'calendar': case 'event': @@ -26,8 +27,8 @@ if($idtype == 'event' && !OC_Calendar_App::getEventObject($id)){ OCP\JSON::error(array('message'=>'permission denied')); exit; } -$sharewith = $_GET['sharewith']; -$sharetype = strip_tags($_GET['sharetype']); +$sharewith = $_POST['sharewith']; +$sharetype = strip_tags($_POST['sharetype']); switch($sharetype){ case 'user': case 'group': diff --git a/apps/calendar/ajax/share/dropdown.php b/apps/calendar/ajax/share/dropdown.php index a3b0faca4b..86cf4ac090 100644 --- a/apps/calendar/ajax/share/dropdown.php +++ b/apps/calendar/ajax/share/dropdown.php @@ -7,7 +7,7 @@ */ $user = OCP\USER::getUser(); -$calid = $_GET['calid']; +$calid = $_POST['calid']; $calendar = OC_Calendar_Calendar::find($calid); if($calendar['userid'] != $user){ OCP\JSON::error(); diff --git a/apps/calendar/ajax/share/share.php b/apps/calendar/ajax/share/share.php index babb8ce3f1..77e1ab9d65 100644 --- a/apps/calendar/ajax/share/share.php +++ b/apps/calendar/ajax/share/share.php @@ -5,10 +5,11 @@ * later. * See the COPYING-README file. */ - OCP\JSON::callCheck(); + +OCP\JSON::callCheck(); -$id = strip_tags($_GET['id']); -$idtype = strip_tags($_GET['idtype']); +$id = strip_tags($_POST['id']); +$idtype = strip_tags($_POST['idtype']); switch($idtype){ case 'calendar': case 'event': @@ -25,8 +26,8 @@ if($idtype == 'event' && !OC_Calendar_App::getEventObject($id)){ OCP\JSON::error(array('message'=>'permission denied')); exit; } -$sharewith = $_GET['sharewith']; -$sharetype = strip_tags($_GET['sharetype']); +$sharewith = $_POST['sharewith']; +$sharetype = strip_tags($_POST['sharetype']); switch($sharetype){ case 'user': case 'group': diff --git a/apps/calendar/ajax/share/unshare.php b/apps/calendar/ajax/share/unshare.php index 09264070dd..c7c0611318 100644 --- a/apps/calendar/ajax/share/unshare.php +++ b/apps/calendar/ajax/share/unshare.php @@ -5,10 +5,11 @@ * later. * See the COPYING-README file. */ - OCP\JSON::callCheck(); -$id = strip_tags($_GET['id']); -$idtype = strip_tags($_GET['idtype']); +OCP\JSON::callCheck(); + +$id = strip_tags($_POST['id']); +$idtype = strip_tags($_POST['idtype']); switch($idtype){ case 'calendar': case 'event': @@ -25,8 +26,8 @@ if($idtype == 'event' && !OC_Calendar_App::getEventObject($id)){ OCP\JSON::error(array('message'=>'permission denied')); exit; } -$sharewith = $_GET['sharewith']; -$sharetype = strip_tags($_GET['sharetype']); +$sharewith = $_POST['sharewith']; +$sharetype = strip_tags($_POST['sharetype']); switch($sharetype){ case 'user': case 'group': diff --git a/apps/calendar/appinfo/app.php b/apps/calendar/appinfo/app.php index 69b556af31..0078705578 100644 --- a/apps/calendar/appinfo/app.php +++ b/apps/calendar/appinfo/app.php @@ -4,29 +4,41 @@ OC::$CLASSPATH['OC_Calendar_App'] = 'apps/calendar/lib/app.php'; OC::$CLASSPATH['OC_Calendar_Calendar'] = 'apps/calendar/lib/calendar.php'; OC::$CLASSPATH['OC_Calendar_Object'] = 'apps/calendar/lib/object.php'; OC::$CLASSPATH['OC_Calendar_Hooks'] = 'apps/calendar/lib/hooks.php'; -OC::$CLASSPATH['OC_Connector_Sabre_CalDAV'] = 'apps/calendar/lib/connector_sabre.php'; +OC::$CLASSPATH['OC_Connector_Sabre_CalDAV'] = 'apps/calendar/lib/sabre/backend.php'; +OC::$CLASSPATH['OC_Connector_Sabre_CalDAV_CalendarRoot'] = 'apps/calendar/lib/sabre/calendarroot.php'; +OC::$CLASSPATH['OC_Connector_Sabre_CalDAV_UserCalendars'] = 'apps/calendar/lib/sabre/usercalendars.php'; +OC::$CLASSPATH['OC_Connector_Sabre_CalDAV_Calendar'] = 'apps/calendar/lib/sabre/calendar.php'; +OC::$CLASSPATH['OC_Connector_Sabre_CalDAV_CalendarObject'] = 'apps/calendar/lib/sabre/object.php'; +OC::$CLASSPATH['OC_Calendar_Repeat'] = 'apps/calendar/lib/repeat.php'; OC::$CLASSPATH['OC_Calendar_Share'] = 'apps/calendar/lib/share.php'; OC::$CLASSPATH['OC_Search_Provider_Calendar'] = 'apps/calendar/lib/search.php'; - +OC::$CLASSPATH['OC_Calendar_Export'] = 'apps/calendar/lib/export.php'; +OC::$CLASSPATH['OC_Calendar_Import'] = 'apps/calendar/lib/import.php'; +OC::$CLASSPATH['OC_Share_Backend_Calendar'] = 'apps/calendar/lib/share/calendar.php'; +OC::$CLASSPATH['OC_Share_Backend_Event'] = 'apps/calendar/lib/share/event.php'; //General Hooks OCP\Util::connectHook('OC_User', 'post_createUser', 'OC_Calendar_Hooks', 'createUser'); OCP\Util::connectHook('OC_User', 'post_deleteUser', 'OC_Calendar_Hooks', 'deleteUser'); +//Repeating Events Hooks +OCP\Util::connectHook('OC_Calendar', 'addEvent', 'OC_Calendar_Repeat', 'generate'); +OCP\Util::connectHook('OC_Calendar', 'editEvent', 'OC_Calendar_Repeat', 'update'); +OCP\Util::connectHook('OC_Calendar', 'deleteEvent', 'OC_Calendar_Repeat', 'clean'); +OCP\Util::connectHook('OC_Calendar', 'moveEvent', 'OC_Calendar_Repeat', 'update'); +OCP\Util::connectHook('OC_Calendar', 'deleteCalendar', 'OC_Calendar_Repeat', 'cleanCalendar'); //Sharing Hooks OCP\Util::connectHook('OC_Calendar', 'deleteEvent', 'OC_Calendar_Share', 'post_eventdelete'); OCP\Util::connectHook('OC_Calendar', 'deleteCalendar', 'OC_Calendar_Share', 'post_caldelete'); - OCP\Util::addscript('calendar','loader'); OCP\Util::addscript("3rdparty", "chosen/chosen.jquery.min"); OCP\Util::addStyle("3rdparty", "chosen/chosen"); -OCP\App::register( array( - 'order' => 10, - 'id' => 'calendar', - 'name' => 'Calendar' )); +OCP\Util::addStyle('3rdparty/miniColors', 'jquery.miniColors'); +OCP\Util::addscript('3rdparty/miniColors', 'jquery.miniColors.min'); OCP\App::addNavigationEntry( array( 'id' => 'calendar_index', 'order' => 10, 'href' => OCP\Util::linkTo( 'calendar', 'index.php' ), 'icon' => OCP\Util::imagePath( 'calendar', 'icon.svg' ), 'name' => $l->t('Calendar'))); -OCP\App::registerPersonal('calendar', 'settings'); OC_Search::registerProvider('OC_Search_Provider_Calendar'); +OCP\Share::registerBackend('calendar', 'OC_Share_Backend_Calendar'); +OCP\Share::registerBackend('event', 'OC_Share_Backend_Event'); diff --git a/apps/calendar/appinfo/database.xml b/apps/calendar/appinfo/database.xml index f60319ad43..16e10010d5 100644 --- a/apps/calendar/appinfo/database.xml +++ b/apps/calendar/appinfo/database.xml @@ -290,4 +290,56 @@ + + + *dbprefix*calendar_repeat + + + + + id + integer + 0 + true + 1 + true + 4 + + + + eventid + integer + 0 + true + true + 4 + + + + calid + integer + 0 + true + true + 4 + + + + startdate + timestamp + 0000-00-00 00:00:00 + false + + + + enddate + timestamp + 0000-00-00 00:00:00 + false + + + + +
    + diff --git a/apps/calendar/appinfo/remote.php b/apps/calendar/appinfo/remote.php index d500ec1080..f499d90966 100644 --- a/apps/calendar/appinfo/remote.php +++ b/apps/calendar/appinfo/remote.php @@ -7,8 +7,8 @@ */ OCP\App::checkAppEnabled('calendar'); -if(substr($_SERVER["REQUEST_URI"],0,strlen(OC::$APPSWEBROOT . '/apps/calendar/caldav.php')) == OC::$APPSWEBROOT . '/apps/calendar/caldav.php'){ - $baseuri = OC::$APPSWEBROOT . '/apps/calendar/caldav.php'; +if(substr($_SERVER["REQUEST_URI"],0,strlen(OC_App::getAppWebPath('calendar').'/caldav.php')) == OC_App::getAppWebPath('calendar'). '/caldav.php'){ + $baseuri = OC_App::getAppWebPath('calendar').'/caldav.php'; } // only need authentication apps @@ -21,18 +21,17 @@ $principalBackend = new OC_Connector_Sabre_Principal(); $caldavBackend = new OC_Connector_Sabre_CalDAV(); // Root nodes -$Sabre_CalDAV_Principal_Collection = new Sabre_CalDAV_Principal_Collection($principalBackend); +$Sabre_CalDAV_Principal_Collection = new Sabre_CalDAV_Principal_Collection($principalBackend); $Sabre_CalDAV_Principal_Collection->disableListing = true; // Disable listening -$Sabre_CalDAV_CalendarRootNode = new Sabre_CalDAV_CalendarRootNode($principalBackend, $caldavBackend); -$Sabre_CalDAV_CalendarRootNode->disableListing = true; // Disable listening +$calendarRoot = new OC_Connector_Sabre_CalDAV_CalendarRoot($principalBackend, $caldavBackend); +$calendarRoot->disableListing = true; // Disable listening -$nodes = array( - $Sabre_CalDAV_Principal_Collection, - $Sabre_CalDAV_CalendarRootNode, +$nodes = array( + $Sabre_CalDAV_Principal_Collection, + $calendarRoot, ); - // Fire up server $server = new Sabre_DAV_Server($nodes); $server->setBaseUri($baseuri); @@ -41,6 +40,7 @@ $server->addPlugin(new Sabre_DAV_Auth_Plugin($authBackend,'ownCloud')); $server->addPlugin(new Sabre_CalDAV_Plugin()); $server->addPlugin(new Sabre_DAVACL_Plugin()); $server->addPlugin(new Sabre_DAV_Browser_Plugin(false)); // Show something in the Browser, but no upload +$server->addPlugin(new Sabre_CalDAV_ICSExportPlugin()); // And off we go! $server->exec(); diff --git a/apps/calendar/appinfo/update.php b/apps/calendar/appinfo/update.php index 3b5998d998..0e11c99884 100644 --- a/apps/calendar/appinfo/update.php +++ b/apps/calendar/appinfo/update.php @@ -15,3 +15,10 @@ if (version_compare($installedVersion, '0.2.1', '<')) { $r = $stmt->execute(array($color,$id)); } } +if (version_compare($installedVersion, '0.5', '<')) { + $calendars = OC_Calendar_Calendar::allCalendars(OCP\USER::getUser()); + foreach($calendars as $calendar){ + OC_Calendar_Repeat::cleanCalendar($calendar['id']); + OC_Calendar_Repeat::generateCalendar($calendar['id']); + } +} \ No newline at end of file diff --git a/apps/calendar/appinfo/version b/apps/calendar/appinfo/version index 267577d47e..cb0c939a93 100644 --- a/apps/calendar/appinfo/version +++ b/apps/calendar/appinfo/version @@ -1 +1 @@ -0.4.1 +0.5.2 diff --git a/apps/calendar/calendar.php b/apps/calendar/calendar.php new file mode 100644 index 0000000000..2c0bee9d23 --- /dev/null +++ b/apps/calendar/calendar.php @@ -0,0 +1,12 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ +$l10n = OC_L10N::get('calendar'); +OCP\JSON::checkLoggedIn(); +OCP\JSON::checkAppEnabled('calendar'); +$tmpl = new OCP\Template('calendar', 'part.choosecalendar'); +$tmpl->printpage(); \ No newline at end of file diff --git a/apps/calendar/css/import.css b/apps/calendar/css/import.css new file mode 100644 index 0000000000..fd82006072 --- /dev/null +++ b/apps/calendar/css/import.css @@ -0,0 +1,14 @@ +/** + * Copyright (c) 2012 Georg Ehrke + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ +#calendar_import_newcalform, #calendar_import_mergewarning, #calendar_import_process, #calendar_import_done{display:none;} +#calendar_import_process_message, #calendar_import_status, #calendar_import_form_message, #calendar_import_mergewarning{text-align:center;} +#calendar_import_form_message{font-weight: bold;} +#calendar_import_newcalendar{width:415px;float:right;} +#calendar_import_mergewarning{clear: both;} +#calendar_import_defaultcolors{clear:both;margin: 0 auto;text-align: center;} +.calendar_import_warning{border-color: #fc3333;} +.calendar-colorpicker-color{display:inline-block;width:20px;height:5px;margin: 0 auto;cursor:pointer;border:2px solid transparent;margin-top: 5px;} \ No newline at end of file diff --git a/apps/calendar/css/style.css b/apps/calendar/css/style.css index 373a456563..64a779b9a9 100644 --- a/apps/calendar/css/style.css +++ b/apps/calendar/css/style.css @@ -19,7 +19,7 @@ #loading { display: none;margin: 0;padding:0;margin-top:5px;} -#calendar_holder {position: relative;bottom: 0; right: 0; left: 0; top: 3em;} +#fullcalendar {position: relative;bottom: 0; right: 0; left: 0; top: 3em;} .fc-content{padding:2px 4px;} #listview {margin: 0; padding: 10px; background: #EEEEEE;} #listview #more_before, #listview #more_after {border: 1px solid #1a1a1a; width:25em;padding: 3px;text-align: center;} @@ -40,8 +40,6 @@ .thisday{background: #FFFABC;} .event {position:relative;} .event.colored {border-bottom: 1px solid white;} -.popup {display: none; position: absolute; z-index: 1000; background: #eeeeee; color: #000000; border: 1px solid #1a1a1a; font-size: 90%;} -.event_popup {width: 280px; height: 40px; padding: 10px;} input[type="button"].active {color: #6193CF} #fromtime, #totime { @@ -133,3 +131,12 @@ padding:0 8px 2px; line-height:1.2; margin-bottom:4px; } + +#choosecalendar a.settings{ + margin-top: 25px; + margin-right: 10px; +} + +#fullcalendar{ + overflow: scroll; +} \ No newline at end of file diff --git a/apps/calendar/export.php b/apps/calendar/export.php index 5780d191a5..1374c49cc0 100644 --- a/apps/calendar/export.php +++ b/apps/calendar/export.php @@ -5,35 +5,26 @@ * later. * See the COPYING-README file. */ - - OCP\User::checkLoggedIn(); OCP\App::checkAppEnabled('calendar'); $cal = isset($_GET['calid']) ? $_GET['calid'] : NULL; $event = isset($_GET['eventid']) ? $_GET['eventid'] : NULL; -$nl = "\r\n"; if(isset($cal)){ $calendar = OC_Calendar_App::getCalendar($cal, true); if(!$calendar){ header('HTTP/1.0 404 Not Found'); exit; } - $calobjects = OC_Calendar_Object::all($cal); header('Content-Type: text/Calendar'); - header('Content-Disposition: inline; filename=' . $calendar['displayname'] . '.ics'); - foreach($calobjects as $calobject){ - echo $calobject['calendardata'] . $nl; - } + header('Content-Disposition: inline; filename=' . str_replace(' ', '-', $calendar['displayname']) . '.ics'); + echo OC_Calendar_Export::export($cal, OC_Calendar_Export::CALENDAR); }elseif(isset($event)){ $data = OC_Calendar_App::getEventObject($_GET['eventid'], true); if(!$data){ header('HTTP/1.0 404 Not Found'); exit; } - $calendarid = $data['calendarid']; - $calendar = OC_Calendar_App::getCalendar($calendarid); header('Content-Type: text/Calendar'); - header('Content-Disposition: inline; filename=' . $data['summary'] . '.ics'); - echo $data['calendardata']; -} -?> + header('Content-Disposition: inline; filename=' . str_replace(' ', '-', $data['summary']) . '.ics'); + echo OC_Calendar_Export::export($event, OC_Calendar_Export::EVENT); +} \ No newline at end of file diff --git a/apps/calendar/img/icon.png b/apps/calendar/img/icon.png index eb9e07cbb1..267efd997f 100644 Binary files a/apps/calendar/img/icon.png and b/apps/calendar/img/icon.png differ diff --git a/apps/calendar/import_tmp/Info b/apps/calendar/import_tmp/Info deleted file mode 100644 index abafbce435..0000000000 --- a/apps/calendar/import_tmp/Info +++ /dev/null @@ -1,2 +0,0 @@ -This folder contains static files with the percentage of the import. -Requires write permission diff --git a/apps/calendar/index.php b/apps/calendar/index.php index cf03a7a3cd..a8ad4ab335 100644 --- a/apps/calendar/index.php +++ b/apps/calendar/index.php @@ -5,25 +5,34 @@ * later. * See the COPYING-README file. */ - - OCP\User::checkLoggedIn(); OCP\App::checkAppEnabled('calendar'); // Create default calendar ... -$calendars = OC_Calendar_Calendar::allCalendars(OCP\USER::getUser(), 1); +$calendars = OC_Calendar_Calendar::allCalendars(OCP\USER::getUser(), false); if( count($calendars) == 0){ OC_Calendar_Calendar::addCalendar(OCP\USER::getUser(),'Default calendar'); - $calendars = OC_Calendar_Calendar::allCalendars(OCP\USER::getUser(), 1); + $calendars = OC_Calendar_Calendar::allCalendars(OCP\USER::getUser(), true); } $eventSources = array(); foreach($calendars as $calendar){ - $eventSources[] = OC_Calendar_Calendar::getEventSourceInfo($calendar); + if($calendar['active'] == 1) { + $eventSources[] = OC_Calendar_Calendar::getEventSourceInfo($calendar); + } } -$eventSources[] = array('url' => '?app=calendar&getfile=ajax/events.php?calendar_id=shared_rw', 'backgroundColor' => '#1D2D44', 'borderColor' => '#888', 'textColor' => 'white', 'editable'=>'true'); -$eventSources[] = array('url' => '?app=calendar&getfile=ajax/events.php?calendar_id=shared_r', 'backgroundColor' => '#1D2D44', 'borderColor' => '#888', 'textColor' => 'white', 'editable' => 'false'); +$events_baseURL = OCP\Util::linkTo('calendar', 'ajax/events.php'); +$eventSources[] = array('url' => $events_baseURL.'?calendar_id=shared_rw', + 'backgroundColor' => '#1D2D44', + 'borderColor' => '#888', + 'textColor' => 'white', + 'editable'=>'true'); +$eventSources[] = array('url' => $events_baseURL.'?calendar_id=shared_r', + 'backgroundColor' => '#1D2D44', + 'borderColor' => '#888', + 'textColor' => 'white', + 'editable' => 'false'); OCP\Util::emitHook('OC_Calendar', 'getSources', array('sources' => &$eventSources)); $categories = OC_Calendar_App::getCategoryOptions(); @@ -54,9 +63,9 @@ OCP\Util::addscript('contacts','jquery.multi-autocomplete'); OCP\Util::addscript('','oc-vcategories'); OCP\App::setActiveNavigationEntry('calendar_index'); $tmpl = new OCP\Template('calendar', 'calendar', 'user'); -$tmpl->assign('eventSources', $eventSources); +$tmpl->assign('eventSources', $eventSources,false); $tmpl->assign('categories', $categories); if(array_key_exists('showevent', $_GET)){ - $tmpl->assign('showevent', $_GET['showevent']); + $tmpl->assign('showevent', $_GET['showevent'], false); } $tmpl->printPage(); diff --git a/apps/calendar/js/calendar.js b/apps/calendar/js/calendar.js index efdff52998..23846c89b8 100644 --- a/apps/calendar/js/calendar.js +++ b/apps/calendar/js/calendar.js @@ -18,7 +18,7 @@ Calendar={ startEventDialog:function(){ Calendar.UI.loading(false); $('.tipsy').remove(); - $('#calendar_holder').fullCalendar('unselect'); + $('#fullcalendar').fullCalendar('unselect'); Calendar.UI.lockTime(); $( "#from" ).datepicker({ dateFormat : 'dd-mm-yy' @@ -78,7 +78,7 @@ Calendar={ $('#event').dialog('destroy').remove(); }else{ Calendar.UI.loading(true); - $('#dialog_holder').load(OC.filePath('calendar', 'ajax/event', 'edit.form.php') + '?id=' + id, Calendar.UI.startEventDialog); + $('#dialog_holder').load(OC.filePath('calendar', 'ajax/event', 'edit.form.php'), {id: id}, Calendar.UI.startEventDialog); } }, submitDeleteEventForm:function(url){ @@ -88,7 +88,7 @@ Calendar={ $.post(url, post, function(data){ Calendar.UI.loading(false); if(data.status == 'success'){ - $('#calendar_holder').fullCalendar('removeEvents', $('#event_form input[name=id]').val()); + $('#fullcalendar').fullCalendar('removeEvents', $('#event_form input[name=id]').val()); $('#event').dialog('destroy').remove(); } else { $('#errorbox').html(t('calendar', 'Deletion failed')); @@ -133,7 +133,7 @@ Calendar={ } else if(data.status == 'success'){ $('#event').dialog('destroy').remove(); - $('#calendar_holder').fullCalendar('refetchEvents'); + $('#fullcalendar').fullCalendar('refetchEvents'); } },"json"); }, @@ -148,7 +148,7 @@ Calendar={ console.log("Event moved successfully"); }else{ revertFunc(); - $('#calendar_holder').fullCalendar('refetchEvents'); + $('#fullcalendar').fullCalendar('refetchEvents'); } }); }, @@ -163,7 +163,7 @@ Calendar={ console.log("Event resized successfully"); }else{ revertFunc(); - $('#calendar_holder').fullCalendar('refetchEvents'); + $('#fullcalendar').fullCalendar('refetchEvents'); } }); }, @@ -207,8 +207,7 @@ Calendar={ } }, showCalDAVUrl:function(username, calname){ - $('#caldav_url').val(totalurl + '/' + username + '/' + calname); - $('#caldav_url').val(encodeURI($('#caldav_url').val())); + $('#caldav_url').val(totalurl + '/' + username + '/' + decodeURIComponent(calname)); $('#caldav_url').show(); $("#caldav_url_close").show(); }, @@ -240,11 +239,11 @@ Calendar={ doc_height = $(document).height(), win_height = $(window).height(); if(direction == 'down' && win_height == (doc_height - scroll)){ - $('#calendar_holder').fullCalendar('next'); + $('#fullcalendar').fullCalendar('next'); $(document).scrollTop(0); event.preventDefault(); }else if (direction == 'top' && scroll == 0) { - $('#calendar_holder').fullCalendar('prev'); + $('#fullcalendar').fullCalendar('prev'); $(document).scrollTop(win_height); event.preventDefault(); } @@ -399,9 +398,9 @@ Calendar={ if (data.status == 'success'){ checkbox.checked = data.active == 1; if (data.active == 1){ - $('#calendar_holder').fullCalendar('addEventSource', data.eventSource); + $('#fullcalendar').fullCalendar('addEventSource', data.eventSource); }else{ - $('#calendar_holder').fullCalendar('removeEventSource', data.eventSource.url); + $('#fullcalendar').fullCalendar('removeEventSource', data.eventSource.url); } } }); @@ -414,7 +413,7 @@ Calendar={ }, edit:function(object, calendarid){ var tr = $(document.createElement('tr')) - .load(OC.filePath('calendar', 'ajax/calendar', 'edit.form.php') + "?calendarid="+calendarid, + .load(OC.filePath('calendar', 'ajax/calendar', 'edit.form.php'), {calendarid: calendarid}, function(){Calendar.UI.Calendar.colorPicker(this)}); $(object).closest('tr').after(tr).hide(); }, @@ -427,9 +426,10 @@ Calendar={ function(data) { if (data.status == 'success'){ var url = 'ajax/events.php?calendar_id='+calid; - $('#calendar_holder').fullCalendar('removeEventSource', url); + $('#fullcalendar').fullCalendar('removeEventSource', url); $('#choosecalendar_dialog').dialog('destroy').remove(); Calendar.UI.Calendar.overview(); + $('#fullcalendar').fullCalendar('refetchEvents'); } }); } @@ -456,8 +456,8 @@ Calendar={ function(data){ if(data.status == 'success'){ $(button).closest('tr').prev().html(data.page).show().next().remove(); - $('#calendar_holder').fullCalendar('removeEventSource', data.eventSource.url); - $('#calendar_holder').fullCalendar('addEventSource', data.eventSource); + $('#fullcalendar').fullCalendar('removeEventSource', data.eventSource.url); + $('#fullcalendar').fullCalendar('addEventSource', data.eventSource); if (calendarid == 'new'){ $('#choosecalendar_dialog > table:first').append(''); } @@ -503,14 +503,14 @@ Calendar={ currentid: 'false', idtype: '', activation:function(object,owner,id){ - $.getJSON(OC.filePath('calendar', 'ajax/share', 'activation.php'),{id:id, idtype:'calendar', activation:object.checked?1:0}); - $('#calendar_holder').fullCalendar('refetchEvents'); + $.post(OC.filePath('calendar', 'ajax/share', 'activation.php'),{id:id, idtype:'calendar', activation:object.checked?1:0}); + $('#fullcalendar').fullCalendar('refetchEvents'); }, dropdown:function(userid, calid){ $('.calendar_share_dropdown').remove(); var element = document.getElementById(userid+'_'+calid); $('
    ').appendTo(element); - $.get(OC.filePath('calendar', 'ajax/share', 'dropdown.php') + '?calid=' + calid, function(data){ + $.post(OC.filePath('calendar', 'ajax/share', 'dropdown.php'), {calid: calid}, function(data){ $('.calendar_share_dropdown').html(data); $('.calendar_share_dropdown').show('blind'); $('#share_user').chosen(); @@ -520,7 +520,7 @@ Calendar={ Calendar.UI.Share.idtype = 'calendar'; }, share:function(id, idtype, sharewith, sharetype){ - $.getJSON(OC.filePath('calendar', 'ajax/share', 'share.php'),{id:id, idtype:idtype, sharewith:sharewith, sharetype:sharetype}, function(data){ + $.post(OC.filePath('calendar', 'ajax/share', 'share.php'),{id:id, idtype:idtype, sharewith:sharewith, sharetype:sharetype}, function(data){ if(sharetype == 'public'){ $('#public_token').val(parent.location.protocol+'//'+location.host+OC.linkTo('', 'public.php')+'?service=calendar&t='+data.message); $('#public_token').css('display', 'block'); @@ -528,7 +528,7 @@ Calendar={ }); }, unshare:function(id, idtype, sharewith, sharetype){ - $.getJSON(OC.filePath('calendar', 'ajax/share', 'unshare.php'),{id:id, idtype:idtype, sharewith:sharewith, sharetype:sharetype}, function(){ + $.post(OC.filePath('calendar', 'ajax/share', 'unshare.php'),{id:id, idtype:idtype, sharewith:sharewith, sharetype:sharetype}, function(){ if(sharetype == 'public'){ $('#public_token').val(''); $('#public_token').css('display', 'none'); @@ -536,7 +536,7 @@ Calendar={ }); }, changepermission:function(id, idtype, sharewith, sharetype, permission){ - $.getJSON(OC.filePath('calendar', 'ajax/share', 'changepermission.php'),{id:id, idtype:idtype, sharewith: sharewith, sharetype:sharetype, permission: (permission?1:0)}); + $.post(OC.filePath('calendar', 'ajax/share', 'changepermission.php'),{id:id, idtype:idtype, sharewith: sharewith, sharetype:sharetype, permission: (permission?1:0)}); }, init:function(){ $('.calendar_share_dropdown').live('mouseleave', function(){ @@ -547,7 +547,7 @@ Calendar={ $('#share_user').live('change', function(){ if($('#sharewithuser_' + $('#share_user option:selected').text()).length == 0){ Calendar.UI.Share.share(Calendar.UI.Share.currentid, Calendar.UI.Share.idtype, $('#share_user option:selected').text(), 'user'); - var newitem = '
  • ' + $('#share_user option:selected').text() + '
  • '; + var newitem = '
  • ' + $('#share_user option:selected').text() + '
  • '; $('#sharewithuser_list').append(newitem); $('#sharewithuser_' + $('#share_user option:selected').text() + ' > img').click(function(){ $('#share_user option[value="' + $(this).parent().text() + '"]').removeAttr('disabled'); @@ -562,7 +562,7 @@ Calendar={ $('#share_group').live('change', function(){ if($('#sharewithgroup_' + $('#share_group option:selected').text()).length == 0){ Calendar.UI.Share.share(Calendar.UI.Share.currentid, Calendar.UI.Share.idtype, $('#share_group option:selected').text(), 'group'); - var newitem = '
  • ' + $('#share_group option:selected').text() + '
  • '; + var newitem = '
  • ' + $('#share_group option:selected').text() + '
  • '; $('#sharewithgroup_list').append(newitem); $('#sharewithgroup_' + $('#share_group option:selected').text() + ' > img').click(function(){ $('#share_group option[value="' + $(this).parent().text() + '"]').removeAttr('disabled'); @@ -605,8 +605,53 @@ Calendar={ }); /*var permissions = (this.checked) ? 1 : 0;*/ } + }, + Drop:{ + init:function(){ + if (typeof window.FileReader === 'undefined') { + console.log('The drop-import feature is not supported in your browser :('); + return false; + } + droparea = document.getElementById('fullcalendar'); + droparea.ondrop = function(e){ + e.preventDefault(); + Calendar.UI.Drop.drop(e); + } + console.log('Drop initialized successfully'); + }, + drop:function(e){ + var files = e.dataTransfer.files; + for(var i = 0;i < files.length;i++){ + var file = files[i]; + reader = new FileReader(); + reader.onload = function(event){ + Calendar.UI.Drop.import(event.target.result); + $('#fullcalendar').fullCalendar('refetchEvents'); + } + reader.readAsDataURL(file); + } + }, + import:function(data){ + $.post(OC.filePath('calendar', 'ajax/import', 'dropimport.php'), {'data':data},function(result) { + if(result.status == 'success'){ + $('#fullcalendar').fullCalendar('addEventSource', result.eventSource); + $('#notification').html(result.message); + $('#notification').slideDown(); + window.setTimeout(function(){$('#notification').slideUp();}, 5000); + return true; + }else{ + $('#notification').html(result.message); + $('#notification').slideDown(); + window.setTimeout(function(){$('#notification').slideUp();}, 5000); + } + }); + } } - } + }, + Settings:{ + // + }, + } $.fullCalendar.views.list = ListView; function ListView(element, calendar) { @@ -774,7 +819,7 @@ function ListView(element, calendar) { } $(document).ready(function(){ Calendar.UI.initScroll(); - $('#calendar_holder').fullCalendar({ + $('#fullcalendar').fullCalendar({ header: false, firstDay: firstDay, editable: true, @@ -803,17 +848,17 @@ $(document).ready(function(){ dayNamesShort: dayNamesShort, allDayText: allDayText, viewDisplay: function(view) { - $('#datecontrol_date').html(view.title); + $('#datecontrol_date').val($('

    ').html(view.title).text()); if (view.name != defaultView) { - $.get(OC.filePath('calendar', 'ajax', 'changeview.php') + "?v="+view.name); + $.post(OC.filePath('calendar', 'ajax', 'changeview.php'), {v:view.name}); defaultView = view.name; } Calendar.UI.setViewActive(view.name); if (view.name == 'agendaWeek') { - $('#calendar_holder').fullCalendar('option', 'aspectRatio', 0.1); + $('#fullcalendar').fullCalendar('option', 'aspectRatio', 0.1); } else { - $('#calendar_holder').fullCalendar('option', 'aspectRatio', 1.35); + $('#fullcalendar').fullCalendar('option', 'aspectRatio', 1.35); } }, columnFormat: { @@ -842,26 +887,49 @@ $(document).ready(function(){ loading: Calendar.UI.loading, eventSources: eventSources }); + $('#datecontrol_date').datepicker({ + changeMonth: true, + changeYear: true, + showButtonPanel: true, + beforeShow: function(input, inst) { + var calendar_holder = $('#fullcalendar'); + var date = calendar_holder.fullCalendar('getDate'); + inst.input.datepicker('setDate', date); + inst.input.val(calendar_holder.fullCalendar('getView').title); + return inst; + }, + onSelect: function(value, inst) { + var date = inst.input.datepicker('getDate'); + $('#fullcalendar').fullCalendar('gotoDate', date); + } + }); fillWindow($('#content')); OCCategories.changed = Calendar.UI.categoriesChanged; OCCategories.app = 'calendar'; $('#oneweekview_radio').click(function(){ - $('#calendar_holder').fullCalendar('changeView', 'agendaWeek'); + $('#fullcalendar').fullCalendar('changeView', 'agendaWeek'); }); $('#onemonthview_radio').click(function(){ - $('#calendar_holder').fullCalendar('changeView', 'month'); + $('#fullcalendar').fullCalendar('changeView', 'month'); }); $('#listview_radio').click(function(){ - $('#calendar_holder').fullCalendar('changeView', 'list'); + $('#fullcalendar').fullCalendar('changeView', 'list'); }); $('#today_input').click(function(){ - $('#calendar_holder').fullCalendar('today'); + $('#fullcalendar').fullCalendar('today'); }); $('#datecontrol_left').click(function(){ - $('#calendar_holder').fullCalendar('prev'); + $('#fullcalendar').fullCalendar('prev'); }); $('#datecontrol_right').click(function(){ - $('#calendar_holder').fullCalendar('next'); + $('#fullcalendar').fullCalendar('next'); }); Calendar.UI.Share.init(); + Calendar.UI.Drop.init(); + $('#choosecalendar .generalsettings').on('click keydown', function() { + OC.appSettings({appid:'calendar', loadJS:true, cache:false}); + }); + $('#choosecalendar .calendarsettings').on('click keydown', function() { + OC.appSettings({appid:'calendar', loadJS:true, cache:false, scriptName:'calendar.php'}); + }); }); diff --git a/apps/calendar/js/geo.js b/apps/calendar/js/geo.js index 092d854746..99290d940e 100644 --- a/apps/calendar/js/geo.js +++ b/apps/calendar/js/geo.js @@ -6,7 +6,7 @@ */ if (navigator.geolocation) { navigator.geolocation.getCurrentPosition(function(position) { - $.getJSON(OC.filePath('calendar', 'ajax/settings', 'guesstimezone.php') + '?lat=' + position.coords.latitude + '&long=' + position.coords.longitude, + $.post(OC.filePath('calendar', 'ajax/settings', 'guesstimezone.php'), {lat: position.coords.latitude, lng: position.coords.longitude}, function(data){ if (data.status == 'success' && typeof(data.message) != 'undefined'){ $('#notification').html(data.message); diff --git a/apps/calendar/js/loader.js b/apps/calendar/js/loader.js index 60d92f448e..253abafc42 100644 --- a/apps/calendar/js/loader.js +++ b/apps/calendar/js/loader.js @@ -5,77 +5,175 @@ * See the COPYING-README file. */ Calendar_Import={ - importdialog: function(filename){ - var path = $('#dir').val(); - $('body').append('

    '); - $('#calendar_import').load(OC.filePath('calendar', 'ajax/import', 'dialog.php'), {filename:filename, path:path}, function(){Calendar_Import.initdialog(filename);}); + Store:{ + file: '', + path: '', + id: 0, + method: '', + calname: '', + calcolor: '', + progresskey: '', + percentage: 0 }, - initdialog: function(filename){ - $('#calendar_import_dialog').dialog({ - width : 500, - close : function() { - $(this).dialog('destroy').remove(); - $('#calendar_import').remove(); - } - }); - $('#import_done_button').click(function(){ - $('#calendar_import_dialog').dialog('destroy').remove(); - $('#calendar_import').remove(); - }); - $('#progressbar').progressbar({value: 0}); - $('#startimport').click(function(){ - var filename = $('#filename').val(); - var path = $('#path').val(); - var calid = $('#calendar option:selected').val(); - if($('#calendar option:selected').val() == 'newcal'){ - var method = 'new'; - var calname = $('#newcalendar').val(); - var calname = $.trim(calname); - if(calname == ''){ - $('#newcalendar').css('background-color', '#FF2626'); - $('#newcalendar').focus(function(){ - $('#newcalendar').css('background-color', '#F8F8F8'); - }); - return false; - } - }else{ - var method = 'old'; - } - $('#newcalendar').attr('readonly', 'readonly'); - $('#calendar').attr('disabled', 'disabled'); - var progressfile = $('#progressfile').val(); - $.post(OC.filePath('calendar', 'ajax/import', 'import.php'), {method: String (method), calname: String (calname), path: String (path), file: String (filename), id: String (calid)}, function(data){ - if(data.status == 'success'){ - $('#progressbar').progressbar('option', 'value', 100); - $('#import_done').css('display', 'block'); + Dialog:{ + open: function(filename){ + OC.addStyle('calendar', 'import'); + Calendar_Import.Store.file = filename; + Calendar_Import.Store.path = $('#dir').val(); + $('body').append('
    '); + $('#calendar_import').load(OC.filePath('calendar', 'ajax/import', 'dialog.php'), {filename:Calendar_Import.Store.file, path:Calendar_Import.Store.path},function(){ + Calendar_Import.Dialog.init(); + }); + }, + close: function(){ + Calendar_Import.reset(); + $(this).dialog('destroy').remove(); + $('#calendar_import_dialog').remove(); + }, + init: function(){ + //init dialog + $('#calendar_import_dialog').dialog({ + width : 500, + resizable: false, + close : function() { + Calendar_Import.Dialog.close(); } }); - $('#form_container').css('display', 'none'); - $('#progressbar_container').css('display', 'block'); - window.setTimeout('Calendar_Import.getimportstatus(\'' + progressfile + '\')', 500); - }); - $('#calendar').change(function(){ - if($('#calendar option:selected').val() == 'newcal'){ - $('#newcalform').slideDown('slow'); - }else{ - $('#newcalform').slideUp('slow'); + //init buttons + $('#calendar_import_done').click(function(){ + Calendar_Import.Dialog.close(); + }); + $('#calendar_import_submit').click(function(){ + Calendar_Import.Core.process(); + }); + $('#calendar_import_mergewarning').click(function(){ + $('#calendar_import_newcalendar').attr('value', $('#calendar_import_availablename').val()); + Calendar_Import.Dialog.mergewarning($('#calendar_import_newcalendar').val()); + }); + $('#calendar_import_calendar').change(function(){ + if($('#calendar_import_calendar option:selected').val() == 'newcal'){ + $('#calendar_import_newcalform').slideDown('slow'); + Calendar_Import.Dialog.mergewarning($('#calendar_import_newcalendar').val()); + }else{ + $('#calendar_import_newcalform').slideUp('slow'); + $('#calendar_import_mergewarning').slideUp('slow'); + } + }); + $('#calendar_import_newcalendar').keyup(function(){ + Calendar_Import.Dialog.mergewarning($.trim($('#calendar_import_newcalendar').val())); + }); + $('#calendar_import_newcalendar_color').miniColors({ + letterCase: 'uppercase' + }); + $('.calendar-colorpicker-color').click(function(){ + var str = $(this).attr('rel'); + str = str.substr(1); + $('#calendar_import_newcalendar_color').attr('value', str); + $(".color-picker").miniColors('value', '#' + str); + }); + //init progressbar + $('#calendar_import_progressbar').progressbar({value: Calendar_Import.Store.percentage}); + Calendar_Import.Store.progresskey = $('#calendar_import_progresskey').val(); + }, + mergewarning: function(newcalname){ + $.post(OC.filePath('calendar', 'ajax/import', 'calendarcheck.php'), {calname: newcalname}, function(data){ + if(data.message == 'exists'){ + $('#calendar_import_mergewarning').slideDown('slow'); + }else{ + $('#calendar_import_mergewarning').slideUp('slow'); + } + }); + }, + update: function(){ + if(Calendar_Import.Store.percentage == 100){ + return false; } - }); + $.post(OC.filePath('calendar', 'ajax/import', 'import.php'), {progresskey: Calendar_Import.Store.progresskey, getprogress: true}, function(data){ + if(data.status == 'success'){ + if(data.percent == null){ + return false; + } + Calendar_Import.Store.percentage = parseInt(data.percent); + $('#calendar_import_progressbar').progressbar('option', 'value', parseInt(data.percent)); + if(data.percent < 100 ){ + window.setTimeout('Calendar_Import.Dialog.update()', 250); + }else{ + $('#calendar_import_done').css('display', 'block'); + } + }else{ + $('#calendar_import_progressbar').progressbar('option', 'value', 100); + $('#calendar_import_progressbar > div').css('background-color', '#FF2626'); + $('#calendar_import_status').html(data.message); + } + }); + return 0; + }, + warning: function(selector){ + $(selector).addClass('calendar_import_warning'); + $(selector).focus(function(){ + $(selector).removeClass('calendar_import_warning'); + }); + } }, - getimportstatus: function(progressfile){ - $.get(OC.filePath('calendar', 'import_tmp', progressfile), function(percent){ - $('#progressbar').progressbar('option', 'value', parseInt(percent)); - if(percent < 100){ - window.setTimeout('Calendar_Import.getimportstatus(\'' + progressfile + '\')', 500); - }else{ - $('#import_done').css('display', 'block'); + Core:{ + process: function(){ + var validation = Calendar_Import.Core.prepare(); + if(validation){ + $('#calendar_import_form').css('display', 'none'); + $('#calendar_import_process').css('display', 'block'); + $('#calendar_import_newcalendar').attr('readonly', 'readonly'); + $('#calendar_import_calendar').attr('disabled', 'disabled'); + Calendar_Import.Core.send(); + window.setTimeout('Calendar_Import.Dialog.update()', 250); } - }); + }, + send: function(){ + $.post(OC.filePath('calendar', 'ajax/import', 'import.php'), + {progresskey: Calendar_Import.Store.progresskey, method: String (Calendar_Import.Store.method), calname: String (Calendar_Import.Store.calname), path: String (Calendar_Import.Store.path), file: String (Calendar_Import.Store.file), id: String (Calendar_Import.Store.id), calcolor: String (Calendar_Import.Store.calcolor)}, function(data){ + if(data.status == 'success'){ + $('#calendar_import_progressbar').progressbar('option', 'value', 100); + Calendar_Import.Store.percentage = 100; + $('#calendar_import_done').css('display', 'block'); + $('#calendar_import_status').html(data.message); + }else{ + $('#calendar_import_progressbar').progressbar('option', 'value', 100); + $('#calendar_import_progressbar > div').css('background-color', '#FF2626'); + $('#calendar_import_status').html(data.message); + } + }); + }, + prepare: function(){ + Calendar_Import.Store.id = $('#calendar_import_calendar option:selected').val(); + if($('#calendar_import_calendar option:selected').val() == 'newcal'){ + Calendar_Import.Store.method = 'new'; + Calendar_Import.Store.calname = $.trim($('#calendar_import_newcalendar').val()); + if(Calendar_Import.Store.calname == ''){ + Calendar_Import.Dialog.warning('#calendar_import_newcalendar'); + return false; + } + Calendar_Import.Store.calcolor = $.trim($('#calendar_import_newcalendar_color').val()); + if(Calendar_Import.Store.calcolor == ''){ + Calendar_Import.Store.calcolor = $('.calendar-colorpicker-color:first').attr('rel'); + } + }else{ + Calendar_Import.Store.method = 'old'; + } + return true; + } + }, + reset: function(){ + Calendar_Import.Store.file = ''; + Calendar_Import.Store.path = ''; + Calendar_Import.Store.id = 0; + Calendar_Import.Store.method = ''; + Calendar_Import.Store.calname = ''; + Calendar_Import.Store.progresskey = ''; + Calendar_Import.Store.percentage = 0; } } $(document).ready(function(){ if(typeof FileActions !== 'undefined'){ - FileActions.register('text/calendar','importcal', '', Calendar_Import.importdialog); - FileActions.setDefault('text/calendar','importcal'); + FileActions.register('text/calendar','importCalendar', FileActions.PERMISSION_READ, '', Calendar_Import.Dialog.open); + FileActions.setDefault('text/calendar','importCalendar'); }; }); diff --git a/apps/calendar/js/settings.js b/apps/calendar/js/settings.js index c768a47a79..20753a7b8f 100644 --- a/apps/calendar/js/settings.js +++ b/apps/calendar/js/settings.js @@ -1,11 +1,7 @@ $(document).ready(function(){ $('#timezone').change( function(){ - OC.msg.startSaving('#calendar .msg') - // Serialize the data var post = $( '#timezone' ).serialize(); - $.post( OC.filePath('calendar', 'ajax/settings', 'settimezone.php'), post, function(data){ - //OC.msg.finishedSaving('#calendar .msg', data); - }); + $.post( OC.filePath('calendar', 'ajax/settings', 'settimezone.php'), post, function(data){return;}); return false; }); $('#timezone').chosen(); @@ -34,6 +30,7 @@ $(document).ready(function(){ $.getJSON(OC.filePath('calendar', 'ajax/settings', 'timeformat.php'), function(jsondata, status) { $('#' + jsondata.timeformat).attr('selected',true); $('#timeformat').chosen(); + $('#timeformat_chzn').css('width', '100px'); }); $.getJSON(OC.filePath('calendar', 'ajax/settings', 'gettimezonedetection.php'), function(jsondata, status){ if(jsondata.detection == 'true'){ @@ -43,5 +40,27 @@ $(document).ready(function(){ $.getJSON(OC.filePath('calendar', 'ajax/settings', 'getfirstday.php'), function(jsondata, status) { $('#' + jsondata.firstday).attr('selected',true); $('#firstday').chosen(); + $('#firstday_chzn').css('width', '100px'); }); + $('#cleancalendarcache').click(function(){ + $.getJSON(OC.filePath('calendar', 'ajax/cache', 'rescan.php'), function(){ + calendarcachecheck(); + }); + }); + calendarcachecheck(); + }); +function calendarcachecheck(){ + $.getJSON(OC.filePath('calendar', 'ajax/cache', 'status.php'), function(jsondata, status) { + $('#cleancalendarcache').attr('title', jsondata.l10n.text); + if(jsondata.status == 'success'){ + $('#cleancalendarcache').css('background', '#F8F8F8'); + $('#cleancalendarcache').css('color', '#333'); + $('#cleancalendarcache').css('text-shadow', '#fff 0 1px 0'); + }else{ + $('#cleancalendarcache').css('background', '#DC143C'); + $('#cleancalendarcache').css('color', '#FFFFFF'); + $('#cleancalendarcache').css('text-shadow', '0px 0px 0px #fff, 0px 0px #fff'); + } + }); +} \ No newline at end of file diff --git a/apps/calendar/l10n/ar.php b/apps/calendar/l10n/ar.php index 679f110285..1ca5e0ead5 100644 --- a/apps/calendar/l10n/ar.php +++ b/apps/calendar/l10n/ar.php @@ -1,9 +1,19 @@ "ليس جميع الجداول الزمنيه محفوضه مؤقة", +"Everything seems to be completely cached" => "كل شيء محفوض مؤقة", +"No calendars found." => "لم يتم العثور على جدول الزمني", +"No events found." => "لم يتم العثور على احداث", "Wrong calendar" => "جدول زمني خاطئ", "New Timezone:" => "التوقيت الجديد", "Timezone changed" => "تم تغيير المنطقة الزمنية", "Invalid request" => "طلب غير مفهوم", "Calendar" => "الجدول الزمني", +"ddd" => "ddd", +"ddd M/d" => "ddd M/d", +"dddd M/d" => "ddd M/d", +"MMMM yyyy" => "ddd M/d", +"MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "MMM d[ yyyy]{ '—'[ MMM] d yyyy}", +"dddd, MMM d, yyyy" => "dddd, MMM d, yyyy", "Birthday" => "عيد ميلاد", "Business" => "عمل", "Call" => "إتصال", @@ -19,6 +29,9 @@ "Projects" => "مشاريع", "Questions" => "اسئلة", "Work" => "العمل", +"by" => "من قبل", +"unnamed" => "غير مسمى", +"New Calendar" => "جدول زمني جديد", "Does not repeat" => "لا يعاد", "Daily" => "يومي", "Weekly" => "أسبوعي", @@ -63,8 +76,19 @@ "by day and month" => "حسب اليوم و الشهر", "Date" => "تاريخ", "Cal." => "تقويم", +"Sun." => "أحد", +"Mon." => "أثن.", +"Tue." => "ثلا.", +"Wed." => "أرب.", +"Thu." => "خمي.", +"Fri." => "جمع.", +"Sat." => "سبت", +"Jan." => "ك2", +"Feb." => "شبا.", +"Mar." => "آذا.", +"Apr." => "نيس.", +"May." => "أيا.", "All day" => "كل النهار", -"New Calendar" => "جدول زمني جديد", "Missing fields" => "خانات خالية من المعلومات", "Title" => "عنوان", "From Date" => "من تاريخ", @@ -77,13 +101,15 @@ "Month" => "شهر", "List" => "قائمة", "Today" => "اليوم", -"Calendars" => "الجداول الزمنية", -"There was a fail, while parsing the file." => "لم يتم قراءة الملف بنجاح.", -"Choose active calendars" => "إختر الجدول الزمني الرئيسي", +"Your calendars" => "جداولك الزمنيه", "CalDav Link" => "وصلة CalDav", +"Shared calendars" => "جداول زمنيه مشتركه", +"No shared calendars" => "لا يوجد جداول زمنيه مشتركه", +"Share Calendar" => "شارك الجدول الزمني", "Download" => "تحميل", "Edit" => "تعديل", "Delete" => "حذف", +"shared with you by" => "مشاركه من قبل", "New calendar" => "جدول زمني جديد", "Edit calendar" => "عادل الجدول الزمني", "Displayname" => "الاسم المرئي", @@ -94,8 +120,15 @@ "Cancel" => "إلغاء", "Edit an event" => "عادل حدث", "Export" => "تصدير المعلومات", +"Eventinfo" => "تفاصيل الحدث", +"Repeating" => "يعاد", +"Alarm" => "تنبيه", +"Attendees" => "الحضور", +"Share" => "شارك", "Title of the Event" => "عنوان الحدث", "Category" => "فئة", +"Separate categories with commas" => "افصل الفئات بالفواصل", +"Edit categories" => "عدل الفئات", "All Day Event" => "حدث في يوم كامل", "From" => "من", "To" => "إلى", @@ -116,20 +149,23 @@ "Interval" => "المده الفاصله", "End" => "نهايه", "occurrences" => "الاحداث", -"Import a calendar file" => "أدخل ملف التقويم", -"Please choose the calendar" => "الرجاء إختر الجدول الزمني", "create a new calendar" => "انشاء جدول زمني جديد", +"Import a calendar file" => "أدخل ملف التقويم", "Name of new calendar" => "أسم الجدول الزمني الجديد", "Import" => "إدخال", -"Importing calendar" => "يتم ادخال الجدول الزمني", -"Calendar imported successfully" => "تم ادخال الجدول الزمني بنجاح", "Close Dialog" => "أغلق الحوار", "Create a new event" => "إضافة حدث جديد", -"Select category" => "اختر الفئة", +"View an event" => "شاهد الحدث", +"No categories selected" => "لم يتم اختيار الفئات", +"of" => "من", +"at" => "في", "Timezone" => "المنطقة الزمنية", -"Check always for changes of the timezone" => "راقب دائما تغير التقويم الزمني", -"Timeformat" => "شكل الوقت", "24h" => "24 ساعة", "12h" => "12 ساعة", -"Calendar CalDAV syncing address:" => "عنوان لتحديث ال CalDAV الجدول الزمني" +"Users" => "المستخدمين", +"select users" => "اختر المستخدمين", +"Editable" => "يمكن تعديله", +"Groups" => "مجموعات", +"select groups" => "اختر المجموعات", +"make public" => "حدث عام" ); diff --git a/apps/calendar/l10n/bg_BG.php b/apps/calendar/l10n/bg_BG.php index e4f73d24a9..fc353ebef9 100644 --- a/apps/calendar/l10n/bg_BG.php +++ b/apps/calendar/l10n/bg_BG.php @@ -1,7 +1,23 @@ "Не са открити календари.", +"No events found." => "Не са открити събития.", +"Import failed" => "Грешка при внасяне", +"New Timezone:" => "Нов часови пояс:", "Timezone changed" => "Часовата зона е сменена", "Invalid request" => "Невалидна заявка", "Calendar" => "Календар", +"Birthday" => "Роджен ден", +"Clients" => "Клиенти", +"Holidays" => "Празници", +"Ideas" => "Идеи", +"Journey" => "Пътуване", +"Meeting" => "Среща", +"Other" => "Друго", +"Personal" => "Лично", +"Projects" => "Проекти", +"Questions" => "Въпроси", +"Work" => "Работа", +"New Calendar" => "Нов календар", "Does not repeat" => "Не се повтаря", "Daily" => "Дневно", "Weekly" => "Седмично", @@ -9,32 +25,60 @@ "Bi-Weekly" => "Двуседмично", "Monthly" => "Месечно", "Yearly" => "Годишно", +"never" => "никога", +"Monday" => "Понеделник", +"Tuesday" => "Вторник", +"Wednesday" => "Сряда", +"Thursday" => "Четвъртък", +"Friday" => "Петък", +"Saturday" => "Събота", +"Sunday" => "Неделя", "All day" => "Всички дни", +"Missing fields" => "Липсват полета", "Title" => "Заглавие", "Week" => "Седмица", "Month" => "Месец", +"List" => "Списък", "Today" => "Днес", -"Calendars" => "Календари", -"There was a fail, while parsing the file." => "Възникна проблем с разлистването на файла.", -"Choose active calendars" => "Изберете активен календар", +"Your calendars" => "Вашите календари", +"Shared calendars" => "Споделени календари", +"No shared calendars" => "Няма споделени календари", +"Share Calendar" => "Споделяне на календар", "Download" => "Изтегляне", "Edit" => "Промяна", +"Delete" => "Изтриване", +"New calendar" => "Нов календар", "Edit calendar" => "Промени календар", "Displayname" => "Екранно име", "Active" => "Активен", "Calendar color" => "Цвят на календара", +"Save" => "Запис", "Submit" => "Продължи", +"Cancel" => "Отказ", "Edit an event" => "Промяна на събитие", +"Export" => "Изнасяне", +"Share" => "Споделяне", "Title of the Event" => "Наименование", "Category" => "Категория", +"Separate categories with commas" => "Отделете категориите със запетаи", +"Edit categories" => "Редактиране на категориите", "All Day Event" => "Целодневно събитие", "From" => "От", "To" => "До", +"Advanced options" => "Разширени настройки", "Location" => "Локация", "Location of the Event" => "Локация", "Description" => "Описание", "Description of the Event" => "Описание", "Repeat" => "Повтори", +"create a new calendar" => "създаване на нов календар", +"Please choose a calendar" => "Изберете календар", +"Name of new calendar" => "Име на новия календар", +"Import" => "Внасяне", +"Close Dialog" => "Затваряне на прозореца", "Create a new event" => "Ново събитие", -"Timezone" => "Часова зона" +"View an event" => "Преглед на събитие", +"No categories selected" => "Няма избрани категории", +"Timezone" => "Часова зона", +"Groups" => "Групи" ); diff --git a/apps/calendar/l10n/ca.php b/apps/calendar/l10n/ca.php index afb1c799d9..9e267604e6 100644 --- a/apps/calendar/l10n/ca.php +++ b/apps/calendar/l10n/ca.php @@ -1,12 +1,23 @@ "No tots els calendaris estan en memòria", +"Everything seems to be completely cached" => "Sembla que tot està en memòria", "No calendars found." => "No s'han trobat calendaris.", "No events found." => "No s'han trobat events.", "Wrong calendar" => "Calendari erroni", +"The file contained either no events or all events are already saved in your calendar." => "El fitxer no contenia esdeveniments o aquests ja estaven desats en el vostre caledari", +"events has been saved in the new calendar" => "els esdeveniments s'han desat en el calendari nou", +"Import failed" => "Ha fallat la importació", +"events has been saved in your calendar" => "els esdveniments s'han desat en el calendari", "New Timezone:" => "Nova zona horària:", "Timezone changed" => "La zona horària ha canviat", "Invalid request" => "Sol.licitud no vàlida", "Calendar" => "Calendari", -"MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "MMM d[ yyyy]{ '—'[ MMM] d yyyy}", +"ddd" => "ddd", +"ddd M/d" => "ddd d/M", +"dddd M/d" => "dddd d/M", +"MMMM yyyy" => "MMMM yyyy", +"MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "d [MMM ][yyyy ]{'—' d MMM yyyy}", +"dddd, MMM d, yyyy" => "dddd, d MMM, yyyy", "Birthday" => "Aniversari", "Business" => "Feina", "Call" => "Trucada", @@ -22,7 +33,9 @@ "Projects" => "Projectes", "Questions" => "Preguntes", "Work" => "Feina", +"by" => "per", "unnamed" => "sense nom", +"New Calendar" => "Calendari nou", "Does not repeat" => "No es repeteix", "Daily" => "Diari", "Weekly" => "Mensual", @@ -67,8 +80,26 @@ "by day and month" => "per dia del mes", "Date" => "Data", "Cal." => "Cal.", +"Sun." => "Dg.", +"Mon." => "Dl.", +"Tue." => "Dm.", +"Wed." => "Dc.", +"Thu." => "Dj.", +"Fri." => "Dv.", +"Sat." => "Ds.", +"Jan." => "Gen.", +"Feb." => "Febr.", +"Mar." => "Març", +"Apr." => "Abr.", +"May." => "Maig", +"Jun." => "Juny", +"Jul." => "Jul.", +"Aug." => "Ag.", +"Sep." => "Set.", +"Oct." => "Oct.", +"Nov." => "Nov.", +"Dec." => "Des.", "All day" => "Tot el dia", -"New Calendar" => "Calendari nou", "Missing fields" => "Els camps que falten", "Title" => "Títol", "From Date" => "Des de la data", @@ -81,9 +112,7 @@ "Month" => "Mes", "List" => "Llista", "Today" => "Avui", -"Calendars" => "Calendaris", -"There was a fail, while parsing the file." => "S'ha produït un error en analitzar el fitxer.", -"Choose active calendars" => "Seleccioneu calendaris actius", +"Settings" => "Configuració", "Your calendars" => "Els vostres calendaris", "CalDav Link" => "Enllaç CalDav", "Shared calendars" => "Calendaris compartits", @@ -132,27 +161,34 @@ "Interval" => "Interval", "End" => "Final", "occurrences" => "aparicions", -"Import a calendar file" => "Importa un fitxer de calendari", -"Please choose the calendar" => "Escolliu el calendari", "create a new calendar" => "crea un nou calendari", +"Import a calendar file" => "Importa un fitxer de calendari", +"Please choose a calendar" => "Escolliu un calendari", "Name of new calendar" => "Nom del nou calendari", +"Take an available name!" => "Escolliu un nom disponible!", +"A Calendar with this name already exists. If you continue anyhow, these calendars will be merged." => "Ja hi ha un calendari amb aquest nom. Si continueu, els calendaris es combinaran.", "Import" => "Importa", -"Importing calendar" => "S'està important el calendari", -"Calendar imported successfully" => "El calendari s'ha importat amb èxit", "Close Dialog" => "Tanca el diàleg", "Create a new event" => "Crea un nou esdeveniment", "View an event" => "Mostra un event", "No categories selected" => "No hi ha categories seleccionades", -"Select category" => "Seleccioneu categoria", "of" => "de", "at" => "a", +"General" => "General", "Timezone" => "Zona horària", -"Check always for changes of the timezone" => "Comprova sempre en els canvis de zona horària", -"Timeformat" => "Format de temps", +"Update timezone automatically" => "Actualitza la zona horària automàticament", +"Time format" => "Format horari", "24h" => "24h", "12h" => "12h", -"First day of the week" => "Primer dia de la setmana", -"Calendar CalDAV syncing address:" => "Adreça de sincronització del calendari CalDAV:", +"Start week on" => "Comença la setmana en ", +"Cache" => "Memòria de cau", +"Clear cache for repeating events" => "Neteja la memòria de cau pels esdeveniments amb repetició", +"URLs" => "URLs", +"Calendar CalDAV syncing addresses" => "Adreça de sincronització del calendari CalDAV", +"more info" => "més informació", +"Primary address (Kontact et al)" => "Adreça primària (Kontact et al)", +"iOS/OS X" => "IOS/OS X", +"Read only iCalendar link(s)" => "Enllaç(os) iCalendar només de lectura", "Users" => "Usuaris", "select users" => "seleccioneu usuaris", "Editable" => "Editable", diff --git a/apps/calendar/l10n/cs_CZ.php b/apps/calendar/l10n/cs_CZ.php index 05d286d82c..ab76cc49d1 100644 --- a/apps/calendar/l10n/cs_CZ.php +++ b/apps/calendar/l10n/cs_CZ.php @@ -1,12 +1,23 @@ "V paměti nejsou uloženy kompletně všechny kalendáře", +"Everything seems to be completely cached" => "Zdá se, že vše je kompletně uloženo v paměti", "No calendars found." => "Žádné kalendáře nenalezeny.", "No events found." => "Žádné události nenalezeny.", "Wrong calendar" => "Nesprávný kalendář", +"The file contained either no events or all events are already saved in your calendar." => "Soubor, obsahující všechny záznamy nebo je prázdný, je již uložen ve Vašem kalendáři.", +"events has been saved in the new calendar" => "Záznam byl uložen v novém kalendáři", +"Import failed" => "Import selhal", +"events has been saved in your calendar" => "záznamů bylo uloženo ve Vašem kalendáři", "New Timezone:" => "Nová časová zóna:", "Timezone changed" => "Časová zóna byla změněna", "Invalid request" => "Chybný požadavek", "Calendar" => "Kalendář", +"ddd" => "ddd", +"ddd M/d" => "ddd M/d", +"dddd M/d" => "dddd M/d", +"MMMM yyyy" => "MMMM rrrr", "MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "d. MMM[ yyyy]{ '—' d.[ MMM] yyyy}", +"dddd, MMM d, yyyy" => "dddd, MMM d, rrrr", "Birthday" => "Narozeniny", "Business" => "Obchodní", "Call" => "Hovor", @@ -22,7 +33,9 @@ "Projects" => "Projekty", "Questions" => "Dotazy", "Work" => "Pracovní", +"by" => "od", "unnamed" => "nepojmenováno", +"New Calendar" => "Nový kalendář", "Does not repeat" => "Neopakuje se", "Daily" => "Denně", "Weekly" => "Týdně", @@ -67,8 +80,26 @@ "by day and month" => "podle dne a měsíce", "Date" => "Datum", "Cal." => "Kal.", +"Sun." => "Ne", +"Mon." => "Po", +"Tue." => "Út", +"Wed." => "St", +"Thu." => "Čt", +"Fri." => "Pá", +"Sat." => "So", +"Jan." => "Ne", +"Feb." => "únor", +"Mar." => "březen", +"Apr." => "duben", +"May." => "květen", +"Jun." => "červen", +"Jul." => "červenec", +"Aug." => "srpen", +"Sep." => "září", +"Oct." => "říjen", +"Nov." => "listopad", +"Dec." => "prosinec", "All day" => "Celý den", -"New Calendar" => "Nový kalendář", "Missing fields" => "Chybějící pole", "Title" => "Název", "From Date" => "Od data", @@ -81,9 +112,7 @@ "Month" => "měsíc", "List" => "Seznam", "Today" => "dnes", -"Calendars" => "Kalendáře", -"There was a fail, while parsing the file." => "Chyba při převodu souboru", -"Choose active calendars" => "Vybrat aktivní kalendář", +"Settings" => "Nastavení", "Your calendars" => "Vaše kalendáře", "CalDav Link" => "CalDav odkaz", "Shared calendars" => "Sdílené kalendáře", @@ -132,27 +161,34 @@ "Interval" => "Interval", "End" => "Konec", "occurrences" => "výskyty", -"Import a calendar file" => "Importovat soubor kalendáře", -"Please choose the calendar" => "Zvolte prosím kalendář", "create a new calendar" => "vytvořit nový kalendář", +"Import a calendar file" => "Importovat soubor kalendáře", +"Please choose a calendar" => "Vyberte prosím kalendář", "Name of new calendar" => "Název nového kalendáře", +"Take an available name!" => "Použijte volné jméno!", +"A Calendar with this name already exists. If you continue anyhow, these calendars will be merged." => "Kalendář s trímto názvem již existuje. Pokud název použijete, stejnojmenné kalendáře budou sloučeny.", "Import" => "Import", -"Importing calendar" => "Kalendář se importuje", -"Calendar imported successfully" => "Kalendář byl úspěšně importován", "Close Dialog" => "Zavřít dialog", "Create a new event" => "Vytvořit novou událost", "View an event" => "Zobrazit událost", "No categories selected" => "Žádné kategorie nevybrány", -"Select category" => "Vyberte kategorii", "of" => "z", "at" => "v", +"General" => "Hlavní", "Timezone" => "Časové pásmo", -"Check always for changes of the timezone" => "Vždy kontrolavat, zda nedošlo ke změně časového pásma", -"Timeformat" => "Formát času", +"Update timezone automatically" => "Obnovit auronaricky časovou zónu.", +"Time format" => "Formát času", "24h" => "24h", "12h" => "12h", -"First day of the week" => "Týden začína v", -"Calendar CalDAV syncing address:" => "Adresa pro synchronizaci kalendáře pomocí CalDAV:", +"Start week on" => "Týden začína v", +"Cache" => "Paměť", +"Clear cache for repeating events" => "Vymazat paměť pro opakuijísí se záznamy", +"URLs" => "URLs", +"Calendar CalDAV syncing addresses" => "Kalendář CalDAV synchronizuje adresy", +"more info" => "podrobnosti", +"Primary address (Kontact et al)" => "Primární adresa (veřejná)", +"iOS/OS X" => "iOS/OS X", +"Read only iCalendar link(s)" => "Odkaz(y) kalendáře pouze pro čtení", "Users" => "Uživatelé", "select users" => "vybrat uživatele", "Editable" => "Upravovatelné", diff --git a/apps/calendar/l10n/da.php b/apps/calendar/l10n/da.php index 36551a2a93..a193d5e156 100644 --- a/apps/calendar/l10n/da.php +++ b/apps/calendar/l10n/da.php @@ -1,12 +1,22 @@ "Ikke alle kalendere er fuldstændig cached", "No calendars found." => "Der blev ikke fundet nogen kalendere.", "No events found." => "Der blev ikke fundet nogen begivenheder.", "Wrong calendar" => "Forkert kalender", +"The file contained either no events or all events are already saved in your calendar." => "Filen indeholdt enten ingen begivenheder eller alle begivenheder er allerede gemt i din kalender.", +"events has been saved in the new calendar" => "begivenheder er gemt i den nye kalender", +"Import failed" => "import mislykkedes", +"events has been saved in your calendar" => "begivenheder er gemt i din kalender", "New Timezone:" => "Ny tidszone:", "Timezone changed" => "Tidszone ændret", "Invalid request" => "Ugyldig forespørgsel", "Calendar" => "Kalender", +"ddd" => "ddd", +"ddd M/d" => "ddd M/d", +"dddd M/d" => "dddd M/d", +"MMMM yyyy" => "MMMM åååå", "MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "MMM d[ åååå]{ '—'[ MMM] d åååå}", +"dddd, MMM d, yyyy" => "dddd, MMM d, åååå", "Birthday" => "Fødselsdag", "Business" => "Forretning", "Call" => "Ring", @@ -22,7 +32,9 @@ "Projects" => "Projekter", "Questions" => "Spørgsmål", "Work" => "Arbejde", +"by" => "af", "unnamed" => "unavngivet", +"New Calendar" => "Ny Kalender", "Does not repeat" => "Gentages ikke", "Daily" => "Daglig", "Weekly" => "Ugentlig", @@ -67,8 +79,26 @@ "by day and month" => "efter dag og måned", "Date" => "Dato", "Cal." => "Kal.", +"Sun." => "Søn.", +"Mon." => "Man.", +"Tue." => "Tir.", +"Wed." => "Ons.", +"Thu." => "Tor.", +"Fri." => "Fre.", +"Sat." => "Lør.", +"Jan." => "Jan.", +"Feb." => "Feb.", +"Mar." => "Mar.", +"Apr." => "Apr.", +"May." => "Maj", +"Jun." => "Jun.", +"Jul." => "Jul.", +"Aug." => "Aug.", +"Sep." => "Sep.", +"Oct." => "Okt.", +"Nov." => "Nov.", +"Dec." => "Dec.", "All day" => "Hele dagen", -"New Calendar" => "Ny Kalender", "Missing fields" => "Manglende felter", "Title" => "Titel", "From Date" => "Fra dato", @@ -81,9 +111,7 @@ "Month" => "Måned", "List" => "Liste", "Today" => "I dag", -"Calendars" => "Kalendere", -"There was a fail, while parsing the file." => "Der opstod en fejl under gennemlæsning af filen.", -"Choose active calendars" => "Vælg aktive kalendere", +"Settings" => "Indstillinger", "Your calendars" => "Dine kalendere", "CalDav Link" => "CalDav-link", "Shared calendars" => "Delte kalendere", @@ -132,10 +160,11 @@ "Interval" => "Interval", "End" => "Afslutning", "occurrences" => "forekomster", -"Import a calendar file" => "Importer en kalenderfil", -"Please choose the calendar" => "Vælg venligst kalender", "create a new calendar" => "opret en ny kalender", +"Import a calendar file" => "Importer en kalenderfil", +"Please choose a calendar" => "Vælg en kalender", "Name of new calendar" => "Navn på ny kalender", +"A Calendar with this name already exists. If you continue anyhow, these calendars will be merged." => "En kalender med dette navn findes allerede. Hvis du fortsætter alligevel, vil disse kalendere blive sammenlagt.", "Import" => "Importer", "Importing calendar" => "Importerer kalender", "Calendar imported successfully" => "Kalender importeret korrekt", @@ -143,16 +172,15 @@ "Create a new event" => "Opret en ny begivenhed", "View an event" => "Vis en begivenhed", "No categories selected" => "Ingen categorier valgt", -"Select category" => "Vælg kategori", "of" => "fra", "at" => "kl.", +"General" => "Generel", "Timezone" => "Tidszone", -"Check always for changes of the timezone" => "Check altid efter ændringer i tidszone", -"Timeformat" => "Tidsformat", +"Update timezone automatically" => "Opdater tidszone automatisk", "24h" => "24T", "12h" => "12T", -"First day of the week" => "Ugens første dag", -"Calendar CalDAV syncing address:" => "Synkroniseringsadresse til CalDAV:", +"more info" => "flere oplysninger", +"iOS/OS X" => "iOS/OS X", "Users" => "Brugere", "select users" => "Vælg brugere", "Editable" => "Redigerbar", diff --git a/apps/calendar/l10n/de.php b/apps/calendar/l10n/de.php index f12a18baad..d91753ff74 100644 --- a/apps/calendar/l10n/de.php +++ b/apps/calendar/l10n/de.php @@ -1,12 +1,23 @@ "Keine Kalender gefunden", -"No events found." => "Keine Termine gefunden", +"Not all calendars are completely cached" => "Noch sind nicht alle Kalender zwischengespeichert.", +"Everything seems to be completely cached" => "Es sieht so aus, als wäre alles vollständig zwischengespeichert.", +"No calendars found." => "Keine Kalender gefunden.", +"No events found." => "Keine Termine gefunden.", "Wrong calendar" => "Falscher Kalender", +"The file contained either no events or all events are already saved in your calendar." => "Entweder enthielt die Datei keine Termine oder alle Termine waren bereits im Kalender gespeichert.", +"events has been saved in the new calendar" => "Der Termin wurde im neuen Kalender gespeichert.", +"Import failed" => "Import fehlgeschlagen", +"events has been saved in your calendar" => "Der Termin wurde im Kalender gespeichert.", "New Timezone:" => "Neue Zeitzone:", "Timezone changed" => "Zeitzone geändert", "Invalid request" => "Fehlerhafte Anfrage", "Calendar" => "Kalender", -"MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "ddd d MMMM[ yyyy]{ -[ddd d] MMMM yyyy}", +"ddd" => "ddd", +"ddd M/d" => "ddd d.M", +"dddd M/d" => "dddd d.M", +"MMMM yyyy" => "MMMM yyyy", +"MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "MMM d[ yyyy]{ '—'[ MMM] d yyyy}", +"dddd, MMM d, yyyy" => "dddd, d. MMM yyyy", "Birthday" => "Geburtstag", "Business" => "Geschäftlich", "Call" => "Anruf", @@ -22,7 +33,9 @@ "Projects" => "Projekte", "Questions" => "Fragen", "Work" => "Arbeit", +"by" => "von", "unnamed" => "unbenannt", +"New Calendar" => "Neuer Kalender", "Does not repeat" => "einmalig", "Daily" => "täglich", "Weekly" => "wöchentlich", @@ -67,8 +80,26 @@ "by day and month" => "nach Tag und Monat", "Date" => "Datum", "Cal." => "Kal.", +"Sun." => "So", +"Mon." => "Mo", +"Tue." => "Di", +"Wed." => "Mi", +"Thu." => "Do", +"Fri." => "Fr", +"Sat." => "Sa", +"Jan." => "Jan.", +"Feb." => "Feb.", +"Mar." => "Mär.", +"Apr." => "Apr.", +"May." => "Mai", +"Jun." => "Jun.", +"Jul." => "Jul.", +"Aug." => "Aug.", +"Sep." => "Sep.", +"Oct." => "Okt.", +"Nov." => "Nov.", +"Dec." => "Dez.", "All day" => "Ganztags", -"New Calendar" => "Neuer Kalender", "Missing fields" => "fehlende Felder", "Title" => "Titel", "From Date" => "Startdatum", @@ -81,12 +112,10 @@ "Month" => "Monat", "List" => "Liste", "Today" => "Heute", -"Calendars" => "Kalender", -"There was a fail, while parsing the file." => "Fehler beim Einlesen der Datei.", -"Choose active calendars" => "Aktive Kalender wählen", +"Settings" => "Einstellungen", "Your calendars" => "Deine Kalender", "CalDav Link" => "CalDAV-Link", -"Shared calendars" => "geteilte Kalender", +"Shared calendars" => "Geteilte Kalender", "No shared calendars" => "Keine geteilten Kalender", "Share Calendar" => "Kalender teilen", "Download" => "Herunterladen", @@ -132,10 +161,12 @@ "Interval" => "Intervall", "End" => "Ende", "occurrences" => "Termine", -"Import a calendar file" => "Kalenderdatei Importieren", -"Please choose the calendar" => "Bitte wählen Sie den Kalender.", "create a new calendar" => "Neuen Kalender anlegen", +"Import a calendar file" => "Kalenderdatei importieren", +"Please choose a calendar" => "Wählen Sie bitte einen Kalender.", "Name of new calendar" => "Kalendername", +"Take an available name!" => "Wählen Sie einen verfügbaren Namen.", +"A Calendar with this name already exists. If you continue anyhow, these calendars will be merged." => "Ein Kalender mit diesem Namen existiert bereits. Sollten Sie fortfahren, werden die beiden Kalender zusammengeführt.", "Import" => "Importieren", "Importing calendar" => "Kalender wird importiert.", "Calendar imported successfully" => "Kalender erfolgreich importiert", @@ -146,15 +177,23 @@ "Select category" => "Kategorie auswählen", "of" => "von", "at" => "um", +"General" => "Allgemein", "Timezone" => "Zeitzone", -"Check always for changes of the timezone" => "immer die Zeitzone überprüfen", -"Timeformat" => "Zeitformat", -"24h" => "24h", -"12h" => "12h", -"First day of the week" => "erster Wochentag", -"Calendar CalDAV syncing address:" => "Kalender CalDAV Synchronisationsadresse:", -"Users" => "Nutzer", -"select users" => "Nutzer auswählen", +"Update timezone automatically" => "Zeitzone automatisch aktualisieren", +"Time format" => "Zeitformat", +"24h" => "24 Stunden", +"12h" => "12 Stunden", +"Start week on" => "Erster Wochentag", +"Cache" => "Zwischenspeicher", +"Clear cache for repeating events" => "Lösche den Zwischenspeicher für wiederholende Veranstaltungen", +"URLs" => "URLs", +"Calendar CalDAV syncing addresses" => "CalDAV-Kalender gleicht Adressen ab", +"more info" => "weitere Informationen", +"Primary address (Kontact et al)" => "Primäre Adresse (Kontakt u.a.)", +"iOS/OS X" => "iOS/OS X", +"Read only iCalendar link(s)" => "Nur lesende(r) iCalender-Link(s)", +"Users" => "Benutzer", +"select users" => "Benutzer auswählen", "Editable" => "editierbar", "Groups" => "Gruppen", "select groups" => "Gruppen auswählen", diff --git a/apps/calendar/l10n/el.php b/apps/calendar/l10n/el.php index 0b289fbcf6..ad07d7b585 100644 --- a/apps/calendar/l10n/el.php +++ b/apps/calendar/l10n/el.php @@ -1,17 +1,28 @@ "Δεν έχει δημιουργηθεί λανθάνουσα μνήμη για όλα τα ημερολόγια", +"Everything seems to be completely cached" => "Όλα έχουν αποθηκευτεί στη cache", "No calendars found." => "Δε βρέθηκαν ημερολόγια.", "No events found." => "Δε βρέθηκαν γεγονότα.", "Wrong calendar" => "Λάθος ημερολόγιο", +"The file contained either no events or all events are already saved in your calendar." => "Το αρχείο που περιέχει είτε κανένα γεγονός είτε όλα τα γεγονότα έχουν ήδη αποθηκευτεί στο ημερολόγιό σας.", +"events has been saved in the new calendar" => "τα συμβάντα αποθηκεύτηκαν σε ένα νέο ημερολόγιο", +"Import failed" => "Η εισαγωγή απέτυχε", +"events has been saved in your calendar" => "το συμβάν αποθηκεύτηκε στο ημερολογιό σου", "New Timezone:" => "Νέα ζώνη ώρας:", "Timezone changed" => "Η ζώνη ώρας άλλαξε", "Invalid request" => "Μη έγκυρο αίτημα", "Calendar" => "Ημερολόγιο", +"ddd" => "ddd", +"ddd M/d" => "ddd M/d", +"dddd M/d" => "dddd M/d", +"MMMM yyyy" => "MMMM yyyy", "MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "MMM d[ yyyy]{ '—'[ MMM] d yyyy}", +"dddd, MMM d, yyyy" => "dddd, MMM d, yyyy", "Birthday" => "Γενέθλια", "Business" => "Επιχείρηση", "Call" => "Κλήση", "Clients" => "Πελάτες", -"Deliverer" => "Παραδώσας", +"Deliverer" => "Προμηθευτής", "Holidays" => "Διακοπές", "Ideas" => "Ιδέες", "Journey" => "Ταξίδι", @@ -22,7 +33,9 @@ "Projects" => "Έργα", "Questions" => "Ερωτήσεις", "Work" => "Εργασία", +"by" => "από", "unnamed" => "ανώνυμο", +"New Calendar" => "Νέα Ημερολόγιο", "Does not repeat" => "Μη επαναλαμβανόμενο", "Daily" => "Καθημερινά", "Weekly" => "Εβδομαδιαία", @@ -67,8 +80,26 @@ "by day and month" => "κατά ημέρα και μήνα", "Date" => "Ημερομηνία", "Cal." => "Ημερ.", +"Sun." => "Κυρ.", +"Mon." => "Δευ.", +"Tue." => "Τρί.", +"Wed." => "Τετ.", +"Thu." => "Πέμ.", +"Fri." => "Παρ.", +"Sat." => "Σάβ.", +"Jan." => "Ιαν.", +"Feb." => "Φεβ.", +"Mar." => "Μάρ.", +"Apr." => "Απρ.", +"May." => "Μαΐ.", +"Jun." => "Ιούν.", +"Jul." => "Ιούλ.", +"Aug." => "Αύγ.", +"Sep." => "Σεπ.", +"Oct." => "Οκτ.", +"Nov." => "Νοέ.", +"Dec." => "Δεκ.", "All day" => "Ολοήμερο", -"New Calendar" => "Νέα Ημερολόγιο", "Missing fields" => "Πεδία που λείπουν", "Title" => "Τίτλος", "From Date" => "Από Ημερομηνία", @@ -81,9 +112,6 @@ "Month" => "Μήνας", "List" => "Λίστα", "Today" => "Σήμερα", -"Calendars" => "Ημερολόγια", -"There was a fail, while parsing the file." => "Υπήρξε μια αποτυχία, κατά την σάρωση του αρχείου.", -"Choose active calendars" => "Επιλέξτε τα ενεργά ημερολόγια", "Your calendars" => "Τα ημερολόγια σου", "CalDav Link" => "Σύνδεση CalDAV", "Shared calendars" => "Κοινόχρηστα ημερολόγια", @@ -132,27 +160,29 @@ "Interval" => "Διάστημα", "End" => "Τέλος", "occurrences" => "περιστατικά", -"Import a calendar file" => "Εισαγωγή αρχείου ημερολογίου", -"Please choose the calendar" => "Παρακαλώ επιλέξτε το ημερολόγιο", "create a new calendar" => "δημιουργία νέου ημερολογίου", +"Import a calendar file" => "Εισαγωγή αρχείου ημερολογίου", +"Please choose a calendar" => "Παρακαλώ επέλεξε ένα ημερολόγιο", "Name of new calendar" => "Όνομα νέου ημερολογίου", +"Take an available name!" => "Επέλεξε ένα διαθέσιμο όνομα!", +"A Calendar with this name already exists. If you continue anyhow, these calendars will be merged." => "Ένα ημερολόγιο με αυτό το όνομα υπάρχει ήδη. Εάν θέλετε να συνεχίσετε, αυτά τα 2 ημερολόγια θα συγχωνευθούν.", "Import" => "Εισαγωγή", -"Importing calendar" => "Εισαγωγή ημερολογίου", -"Calendar imported successfully" => "Το ημερολόγιο εισήχθει επιτυχώς", "Close Dialog" => "Κλείσιμο Διαλόγου", "Create a new event" => "Δημιουργήστε ένα νέο συμβάν", "View an event" => "Εμφάνισε ένα γεγονός", "No categories selected" => "Δεν επελέγησαν κατηγορίες", -"Select category" => "Επιλέξτε κατηγορία", "of" => "του", "at" => "στο", "Timezone" => "Ζώνη ώρας", -"Check always for changes of the timezone" => "Έλεγχος πάντα για τις αλλαγές της ζώνης ώρας", -"Timeformat" => "Μορφή ώρας", "24h" => "24ω", "12h" => "12ω", -"First day of the week" => "Πρώτη μέρα της εβδομάδας", -"Calendar CalDAV syncing address:" => "Διεύθυνση για το συγχρονισμού του ημερολογίου CalDAV:", +"Cache" => "Cache", +"Clear cache for repeating events" => "Εκκαθάριση λανθάνουσας μνήμης για επανάληψη γεγονότων", +"Calendar CalDAV syncing addresses" => "Διευθύνσεις συγχρονισμού ημερολογίου CalDAV", +"more info" => "περισσότερες πλροφορίες", +"Primary address (Kontact et al)" => "Κύρια Διεύθυνση(Επαφή και άλλα)", +"iOS/OS X" => "iOS/OS X", +"Read only iCalendar link(s)" => " iCalendar link(s) μόνο για ανάγνωση", "Users" => "Χρήστες", "select users" => "επέλεξε χρήστες", "Editable" => "Επεξεργάσιμο", diff --git a/apps/calendar/l10n/eo.php b/apps/calendar/l10n/eo.php index b1127d59ca..be3db9bfae 100644 --- a/apps/calendar/l10n/eo.php +++ b/apps/calendar/l10n/eo.php @@ -1,12 +1,23 @@ "Ne ĉiuj kalendaroj estas tute kaŝmemorigitaj", +"Everything seems to be completely cached" => "Ĉio ŝajnas tute kaŝmemorigita", "No calendars found." => "Neniu kalendaro troviĝis.", "No events found." => "Neniu okazaĵo troviĝis.", "Wrong calendar" => "Malĝusta kalendaro", -"New Timezone:" => "Nova horzono:", +"The file contained either no events or all events are already saved in your calendar." => "Aŭ la dosiero enhavas neniun okazaĵon aŭ ĉiuj okazaĵoj jam estas konservitaj en via kalendaro.", +"events has been saved in the new calendar" => "okazaĵoj estas konservitaj en la nova kalendaro", +"Import failed" => "Enporto malsukcesis", +"events has been saved in your calendar" => "okazaĵoj estas konservitaj en via kalendaro", +"New Timezone:" => "Nova horozono:", "Timezone changed" => "La horozono estas ŝanĝita", "Invalid request" => "Nevalida peto", "Calendar" => "Kalendaro", +"ddd" => "ddd", +"ddd M/d" => "ddd d/M", +"dddd M/d" => "dddd d/M", +"MMMM yyyy" => "MMMM yyyy", "MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "d MMM[ yyyy]{ '—'d[ MMM] yyyy}", +"dddd, MMM d, yyyy" => "dddd, d-a de MMM yyyy", "Birthday" => "Naskiĝotago", "Business" => "Negoco", "Call" => "Voko", @@ -22,7 +33,9 @@ "Projects" => "Projektoj", "Questions" => "Demandoj", "Work" => "Laboro", +"by" => "de", "unnamed" => "nenomita", +"New Calendar" => "Nova kalendaro", "Does not repeat" => "Ĉi tio ne ripetiĝas", "Daily" => "Tage", "Weekly" => "Semajne", @@ -67,8 +80,26 @@ "by day and month" => "laŭ tago kaj monato", "Date" => "Dato", "Cal." => "Kal.", +"Sun." => "dim.", +"Mon." => "lun.", +"Tue." => "mar.", +"Wed." => "mer.", +"Thu." => "ĵaŭ.", +"Fri." => "ven.", +"Sat." => "sab.", +"Jan." => "Jan.", +"Feb." => "Feb.", +"Mar." => "Mar.", +"Apr." => "Apr.", +"May." => "Maj.", +"Jun." => "Jun.", +"Jul." => "Jul.", +"Aug." => "Aŭg.", +"Sep." => "Sep.", +"Oct." => "Okt.", +"Nov." => "Nov.", +"Dec." => "Dec.", "All day" => "La tuta tago", -"New Calendar" => "Nova kalendaro", "Missing fields" => "Mankas iuj kampoj", "Title" => "Titolo", "From Date" => "ekde la dato", @@ -81,9 +112,7 @@ "Month" => "Monato", "List" => "Listo", "Today" => "Hodiaŭ", -"Calendars" => "Kalendaroj", -"There was a fail, while parsing the file." => "Malsukceso okazis dum analizo de la dosiero.", -"Choose active calendars" => "Elektu aktivajn kalendarojn", +"Settings" => "Agordo", "Your calendars" => "Viaj kalendaroj", "CalDav Link" => "CalDav-a ligilo", "Shared calendars" => "Kunhavigitaj kalendaroj", @@ -132,27 +161,34 @@ "Interval" => "Intervalo", "End" => "Fino", "occurrences" => "aperoj", -"Import a calendar file" => "Enporti kalendarodosieron", -"Please choose the calendar" => "Bonvolu elekti kalendaron", "create a new calendar" => "Krei novan kalendaron", +"Import a calendar file" => "Enporti kalendarodosieron", +"Please choose a calendar" => "Bonvolu elekti kalendaron", "Name of new calendar" => "Nomo de la nova kalendaro", +"Take an available name!" => "Prenu haveblan nomon!", +"A Calendar with this name already exists. If you continue anyhow, these calendars will be merged." => "Kalendaro kun ĉi tiu nomo jam ekzastas. Se vi malgraŭe daŭros, ĉi tiuj kalendaroj kunfandiĝos.", "Import" => "Enporti", -"Importing calendar" => "Kalendaro estas enportata", -"Calendar imported successfully" => "La kalendaro enportiĝis sukcese", "Close Dialog" => "Fermi la dialogon", "Create a new event" => "Krei okazaĵon", "View an event" => "Vidi okazaĵon", "No categories selected" => "Neniu kategorio elektita", -"Select category" => "Elekti kategorion", "of" => "de", "at" => "ĉe", +"General" => "Ĝenerala", "Timezone" => "Horozono", -"Check always for changes of the timezone" => "Ĉiam kontroli ĉu la horzono ŝanĝiĝis", -"Timeformat" => "Tempoformo", +"Update timezone automatically" => "Aŭtomate ĝisdatigi la horozonon", +"Time format" => "Horoformo", "24h" => "24h", "12h" => "12h", -"First day of the week" => "Unua tago de la semajno", -"Calendar CalDAV syncing address:" => "Adreso de kalendarosinkronigo per CalDAV:", +"Start week on" => "Komenci semajnon je", +"Cache" => "Kaŝmemoro", +"Clear cache for repeating events" => "Forviŝi kaŝmemoron por ripeto de okazaĵoj", +"URLs" => "URL-oj", +"Calendar CalDAV syncing addresses" => "sinkronigaj adresoj por CalDAV-kalendaroj", +"more info" => "pli da informo", +"Primary address (Kontact et al)" => "Ĉefa adreso (Kontact kaj aliaj)", +"iOS/OS X" => "iOS/OS X", +"Read only iCalendar link(s)" => "Nurlegebla(j) iCalendar-ligilo(j)", "Users" => "Uzantoj", "select users" => "elekti uzantojn", "Editable" => "Redaktebla", diff --git a/apps/calendar/l10n/es.php b/apps/calendar/l10n/es.php index 4cd9e3202b..3ebcd2e943 100644 --- a/apps/calendar/l10n/es.php +++ b/apps/calendar/l10n/es.php @@ -1,12 +1,23 @@ "Aún no se han guardado en caché todos los calendarios", +"Everything seems to be completely cached" => "Parece que se ha guardado todo en caché", "No calendars found." => "No se encontraron calendarios.", "No events found." => "No se encontraron eventos.", "Wrong calendar" => "Calendario incorrecto", +"The file contained either no events or all events are already saved in your calendar." => "El archivo no contiene eventos o ya existen en tu calendario.", +"events has been saved in the new calendar" => "Los eventos han sido guardados en el nuevo calendario", +"Import failed" => "Fallo en la importación", +"events has been saved in your calendar" => "eventos se han guardado en tu calendario", "New Timezone:" => "Nueva zona horaria:", "Timezone changed" => "Zona horaria cambiada", "Invalid request" => "Petición no válida", "Calendar" => "Calendario", +"ddd" => "ddd", +"ddd M/d" => "ddd M/d", +"dddd M/d" => "dddd M/d", +"MMMM yyyy" => "MMMM yyyy", "MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "MMM d[ yyyy]{ '—'[ MMM] d yyyy}", +"dddd, MMM d, yyyy" => "dddd, MMM d, yyyy", "Birthday" => "Cumpleaños", "Business" => "Negocios", "Call" => "Llamada", @@ -22,7 +33,9 @@ "Projects" => "Proyectos", "Questions" => "Preguntas", "Work" => "Trabajo", +"by" => "por", "unnamed" => "Sin nombre", +"New Calendar" => "Nuevo calendario", "Does not repeat" => "No se repite", "Daily" => "Diariamente", "Weekly" => "Semanalmente", @@ -67,8 +80,26 @@ "by day and month" => "por día y mes", "Date" => "Fecha", "Cal." => "Cal.", +"Sun." => "Dom.", +"Mon." => "Lun.", +"Tue." => "Mar.", +"Wed." => "Mier.", +"Thu." => "Jue.", +"Fri." => "Vie.", +"Sat." => "Sab.", +"Jan." => "Ene.", +"Feb." => "Feb.", +"Mar." => "Mar.", +"Apr." => "Abr.", +"May." => "May.", +"Jun." => "Jun.", +"Jul." => "Jul.", +"Aug." => "Ago.", +"Sep." => "Sep.", +"Oct." => "Oct.", +"Nov." => "Nov.", +"Dec." => "Dic.", "All day" => "Todo el día", -"New Calendar" => "Nuevo calendario", "Missing fields" => "Los campos que faltan", "Title" => "Título", "From Date" => "Desde la fecha", @@ -81,9 +112,6 @@ "Month" => "Mes", "List" => "Lista", "Today" => "Hoy", -"Calendars" => "Calendarios", -"There was a fail, while parsing the file." => "Se ha producido un fallo al analizar el archivo.", -"Choose active calendars" => "Elige los calendarios activos", "Your calendars" => "Tus calendarios", "CalDav Link" => "Enlace a CalDav", "Shared calendars" => "Calendarios compartidos", @@ -132,27 +160,29 @@ "Interval" => "Intervalo", "End" => "Fin", "occurrences" => "ocurrencias", -"Import a calendar file" => "Importar un archivo de calendario", -"Please choose the calendar" => "Por favor elige el calendario", "create a new calendar" => "Crear un nuevo calendario", +"Import a calendar file" => "Importar un archivo de calendario", +"Please choose a calendar" => "Por favor, escoge un calendario", "Name of new calendar" => "Nombre del nuevo calendario", +"Take an available name!" => "¡Elige un nombre disponible!", +"A Calendar with this name already exists. If you continue anyhow, these calendars will be merged." => "Ya existe un calendario con este nombre. Si continúas, se combinarán los calendarios.", "Import" => "Importar", -"Importing calendar" => "Importando calendario", -"Calendar imported successfully" => "Calendario importado exitosamente", "Close Dialog" => "Cerrar diálogo", "Create a new event" => "Crear un nuevo evento", "View an event" => "Ver un evento", "No categories selected" => "Ninguna categoría seleccionada", -"Select category" => "Seleccionar categoría", "of" => "de", "at" => "a las", "Timezone" => "Zona horaria", -"Check always for changes of the timezone" => "Comprobar siempre por cambios en la zona horaria", -"Timeformat" => "Formato de hora", "24h" => "24h", "12h" => "12h", -"First day of the week" => "Primer día de la semana", -"Calendar CalDAV syncing address:" => "Dirección de sincronización de calendario CalDAV:", +"Cache" => "Caché", +"Clear cache for repeating events" => "Limpiar caché de eventos recurrentes", +"Calendar CalDAV syncing addresses" => "Direcciones de sincronización de calendario CalDAV:", +"more info" => "Más información", +"Primary address (Kontact et al)" => "Dirección principal (Kontact y otros)", +"iOS/OS X" => "iOS/OS X", +"Read only iCalendar link(s)" => "Enlace(s) iCalendar de sólo lectura", "Users" => "Usuarios", "select users" => "seleccionar usuarios", "Editable" => "Editable", diff --git a/apps/calendar/l10n/et_EE.php b/apps/calendar/l10n/et_EE.php index 931ca56f5f..59f494f8ab 100644 --- a/apps/calendar/l10n/et_EE.php +++ b/apps/calendar/l10n/et_EE.php @@ -6,7 +6,12 @@ "Timezone changed" => "Ajavöönd on muudetud", "Invalid request" => "Vigane päring", "Calendar" => "Kalender", +"ddd" => "ddd", +"ddd M/d" => "ddd M/d", +"dddd M/d" => "dddd M/d", +"MMMM yyyy" => "MMMM yyyy", "MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "MMM d[ yyyy]{ '—'[ MMM] d yyyy}", +"dddd, MMM d, yyyy" => "dddd, MMM d, yyyy", "Birthday" => "Sünnipäev", "Business" => "Äri", "Call" => "Helista", @@ -23,6 +28,7 @@ "Questions" => "Küsimused", "Work" => "Töö", "unnamed" => "nimetu", +"New Calendar" => "Uus kalender", "Does not repeat" => "Ei kordu", "Daily" => "Iga päev", "Weekly" => "Iga nädal", @@ -68,7 +74,6 @@ "Date" => "Kuupäev", "Cal." => "Kal.", "All day" => "Kogu päev", -"New Calendar" => "Uus kalender", "Missing fields" => "Puuduvad väljad", "Title" => "Pealkiri", "From Date" => "Alates kuupäevast", @@ -81,9 +86,6 @@ "Month" => "Kuu", "List" => "Nimekiri", "Today" => "Täna", -"Calendars" => "Kalendrid", -"There was a fail, while parsing the file." => "Faili parsimisel tekkis viga.", -"Choose active calendars" => "Vali aktiivsed kalendrid", "Your calendars" => "Sinu kalendrid", "CalDav Link" => "CalDav Link", "Shared calendars" => "Jagatud kalendrid", @@ -132,27 +134,19 @@ "Interval" => "Intervall", "End" => "Lõpp", "occurrences" => "toimumiskordi", -"Import a calendar file" => "Impordi kalendrifail", -"Please choose the calendar" => "Palun vali kalender", "create a new calendar" => "loo uus kalender", +"Import a calendar file" => "Impordi kalendrifail", "Name of new calendar" => "Uue kalendri nimi", "Import" => "Impordi", -"Importing calendar" => "Kalendri importimine", -"Calendar imported successfully" => "Kalender on imporditud", "Close Dialog" => "Sulge dialoogiaken", "Create a new event" => "Loo sündmus", "View an event" => "Vaata üritust", "No categories selected" => "Ühtegi kategooriat pole valitud", -"Select category" => "Salvesta kategooria", "of" => "/", "at" => "kell", "Timezone" => "Ajavöönd", -"Check always for changes of the timezone" => "Kontrolli alati muudatusi ajavööndis", -"Timeformat" => "Aja vorming", "24h" => "24h", "12h" => "12h", -"First day of the week" => "Nädala esimene päev", -"Calendar CalDAV syncing address:" => "Kalendri CalDAV sünkroniseerimise aadress:", "Users" => "Kasutajad", "select users" => "valitud kasutajad", "Editable" => "Muudetav", diff --git a/apps/calendar/l10n/eu.php b/apps/calendar/l10n/eu.php index 9e1300032f..5ebce09c58 100644 --- a/apps/calendar/l10n/eu.php +++ b/apps/calendar/l10n/eu.php @@ -1,11 +1,18 @@ "Egutegi guztiak ez daude guztiz cacheatuta", +"Everything seems to be completely cached" => "Dena guztiz cacheatuta dagoela dirudi", "No calendars found." => "Ez da egutegirik aurkitu.", "No events found." => "Ez da gertaerarik aurkitu.", "Wrong calendar" => "Egutegi okerra", +"The file contained either no events or all events are already saved in your calendar." => "Fitxategiak ez zuen gertaerarik edo gertaera guztiak dagoeneko egutegian gordeta zeuden.", +"events has been saved in the new calendar" => "gertaerak egutegi berrian gorde dira", +"Import failed" => "Inportazioak huts egin du", +"events has been saved in your calendar" => "gertaerak zure egutegian gorde dira", "New Timezone:" => "Ordu-zonalde berria", "Timezone changed" => "Ordu-zonaldea aldatuta", "Invalid request" => "Baliogabeko eskaera", "Calendar" => "Egutegia", +"MMMM yyyy" => "yyyy MMMM", "Birthday" => "Jaioteguna", "Business" => "Negozioa", "Call" => "Deia", @@ -22,6 +29,7 @@ "Questions" => "Galderak", "Work" => "Lana", "unnamed" => "izengabea", +"New Calendar" => "Egutegi berria", "Does not repeat" => "Ez da errepikatzen", "Daily" => "Egunero", "Weekly" => "Astero", @@ -66,8 +74,26 @@ "by day and month" => "eguna eta hilabetearen arabera", "Date" => "Data", "Cal." => "Eg.", +"Sun." => "ig.", +"Mon." => "al.", +"Tue." => "ar.", +"Wed." => "az.", +"Thu." => "og.", +"Fri." => "ol.", +"Sat." => "lr.", +"Jan." => "urt.", +"Feb." => "ots.", +"Mar." => "mar.", +"Apr." => "api.", +"May." => "mai.", +"Jun." => "eka.", +"Jul." => "uzt.", +"Aug." => "abu.", +"Sep." => "ira.", +"Oct." => "urr.", +"Nov." => "aza.", +"Dec." => "abe.", "All day" => "Egun guztia", -"New Calendar" => "Egutegi berria", "Missing fields" => "Eremuak faltan", "Title" => "Izenburua", "From Date" => "Hasierako Data", @@ -80,9 +106,6 @@ "Month" => "Hilabetea", "List" => "Zerrenda", "Today" => "Gaur", -"Calendars" => "Egutegiak", -"There was a fail, while parsing the file." => "Huts bat egon da, fitxategia aztertzen zen bitartea.", -"Choose active calendars" => "Aukeratu egutegi aktiboak", "Your calendars" => "Zure egutegiak", "CalDav Link" => "CalDav lotura", "Shared calendars" => "Elkarbanatutako egutegiak", @@ -131,25 +154,26 @@ "Interval" => "Tartea", "End" => "Amaiera", "occurrences" => "errepikapenak", -"Import a calendar file" => "Inportatu egutegi fitxategi bat", -"Please choose the calendar" => "Mesedez aukeratu egutegia", "create a new calendar" => "sortu egutegi berria", +"Import a calendar file" => "Inportatu egutegi fitxategi bat", +"Please choose a calendar" => "Mesedez aukeratu egutegi bat.", "Name of new calendar" => "Egutegi berriaren izena", +"Take an available name!" => "Hartu eskuragarri dagoen izen bat!", +"A Calendar with this name already exists. If you continue anyhow, these calendars will be merged." => "Izen hau duen egutegi bat dagoeneko existitzen da. Hala ere jarraitzen baduzu, egutegi hauek elkartuko dira.", "Import" => "Importatu", -"Importing calendar" => "Egutegia inportatzen", -"Calendar imported successfully" => "Egutegia ongi inportatu da", "Close Dialog" => "Itxi lehioa", "Create a new event" => "Sortu gertaera berria", "View an event" => "Ikusi gertaera bat", "No categories selected" => "Ez da kategoriarik hautatu", -"Select category" => "Aukeratu kategoria", "Timezone" => "Ordu-zonaldea", -"Check always for changes of the timezone" => "Egiaztatu beti ordu-zonalde aldaketen bila", -"Timeformat" => "Ordu formatua", "24h" => "24h", "12h" => "12h", -"First day of the week" => "Asteko lehenego eguna", -"Calendar CalDAV syncing address:" => "Egutegiaren CalDAV sinkronizazio helbidea", +"Cache" => "Cache", +"Clear cache for repeating events" => "Ezabatu gertaera errepikakorren cachea", +"Calendar CalDAV syncing addresses" => "Egutegiaren CalDAV sinkronizazio helbideak", +"more info" => "informazio gehiago", +"Primary address (Kontact et al)" => "Helbide nagusia", +"iOS/OS X" => "iOS/OS X", "Users" => "Erabiltzaileak", "select users" => "hautatutako erabiltzaileak", "Editable" => "Editagarria", diff --git a/apps/calendar/l10n/fa.php b/apps/calendar/l10n/fa.php index cd2bb9c2e5..9235460834 100644 --- a/apps/calendar/l10n/fa.php +++ b/apps/calendar/l10n/fa.php @@ -6,7 +6,12 @@ "Timezone changed" => "زمان محلی تغییر یافت", "Invalid request" => "درخواست نامعتبر", "Calendar" => "تقویم", +"ddd" => "ddd", +"ddd M/d" => "ddd M/d", +"dddd M/d" => "dddd M/d", +"MMMM yyyy" => "MMMM yyyy", "MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "DDD m[ yyyy]{ '—'[ DDD] m yyyy}", +"dddd, MMM d, yyyy" => "dddd, MMM d, yyyy", "Birthday" => "روزتولد", "Business" => "تجارت", "Call" => "تماس گرفتن", @@ -23,6 +28,7 @@ "Questions" => "سوالات", "Work" => "کار", "unnamed" => "نام گذاری نشده", +"New Calendar" => "تقویم جدید", "Does not repeat" => "تکرار نکنید", "Daily" => "روزانه", "Weekly" => "هفتهگی", @@ -68,7 +74,6 @@ "Date" => "تاریخ", "Cal." => "تقویم.", "All day" => "هرروز", -"New Calendar" => "تقویم جدید", "Missing fields" => "فیلد های گم شده", "Title" => "عنوان", "From Date" => "از تاریخ", @@ -81,9 +86,6 @@ "Month" => "ماه", "List" => "فهرست", "Today" => "امروز", -"Calendars" => "تقویم ها", -"There was a fail, while parsing the file." => "ناتوان در تجزیه پرونده", -"Choose active calendars" => "تقویم فعال را انتخاب کنید", "Your calendars" => "تقویم های شما", "CalDav Link" => "CalDav Link", "Shared calendars" => "تقویمهای به اشترک گذاری شده", @@ -132,27 +134,19 @@ "Interval" => "فاصله", "End" => "پایان", "occurrences" => "ظهور", -"Import a calendar file" => "یک پرونده حاوی تقویم وارد کنید", -"Please choose the calendar" => "لطفا تقویم را انتخاب کنید", "create a new calendar" => "یک تقویم جدید ایجاد کنید", +"Import a calendar file" => "یک پرونده حاوی تقویم وارد کنید", "Name of new calendar" => "نام تقویم جدید", "Import" => "ورودی دادن", -"Importing calendar" => "درحال افزودن تقویم", -"Calendar imported successfully" => "افزودن تقویم موفقیت آمیز بود", "Close Dialog" => "بستن دیالوگ", "Create a new event" => "یک رویداد ایجاد کنید", "View an event" => "دیدن یک رویداد", "No categories selected" => "هیچ گروهی انتخاب نشده", -"Select category" => "انتخاب گروه", "of" => "از", "at" => "در", "Timezone" => "زمان محلی", -"Check always for changes of the timezone" => "همیشه بررسی کنید برای تغییر زمان محلی", -"Timeformat" => "نوع زمان", "24h" => "24 ساعت", "12h" => "12 ساعت", -"First day of the week" => "یکمین روز هفته", -"Calendar CalDAV syncing address:" => "Calendar CalDAV syncing address :", "Users" => "کاربرها", "select users" => "انتخاب شناسه ها", "Editable" => "قابل ویرایش", diff --git a/apps/calendar/l10n/fi_FI.php b/apps/calendar/l10n/fi_FI.php index 4de94b7b7b..c4c9df3588 100644 --- a/apps/calendar/l10n/fi_FI.php +++ b/apps/calendar/l10n/fi_FI.php @@ -2,6 +2,9 @@ "No calendars found." => "Kalentereita ei löytynyt", "No events found." => "Tapahtumia ei löytynyt.", "Wrong calendar" => "Väärä kalenteri", +"The file contained either no events or all events are already saved in your calendar." => "Tiedosto ei joko sisältänyt tapahtumia tai vaihtoehtoisesti kaikki tapahtumat on jo tallennettu kalenteriisi.", +"Import failed" => "Tuonti epäonnistui", +"events has been saved in your calendar" => "tapahtumaa on tallennettu kalenteriisi", "New Timezone:" => "Uusi aikavyöhyke:", "Timezone changed" => "Aikavyöhyke vaihdettu", "Invalid request" => "Virheellinen pyyntö", @@ -21,6 +24,7 @@ "Questions" => "Kysymykset", "Work" => "Työ", "unnamed" => "nimetön", +"New Calendar" => "Uusi kalenteri", "Does not repeat" => "Ei toistoa", "Daily" => "Päivittäin", "Weekly" => "Viikottain", @@ -55,8 +59,26 @@ "November" => "Marraskuu", "December" => "Joulukuu", "Date" => "Päivämäärä", +"Sun." => "Su", +"Mon." => "Ma", +"Tue." => "Ti", +"Wed." => "Ke", +"Thu." => "To", +"Fri." => "Pe", +"Sat." => "La", +"Jan." => "Tammi", +"Feb." => "Helmi", +"Mar." => "Maalis", +"Apr." => "Huhti", +"May." => "Touko", +"Jun." => "Kesä", +"Jul." => "Heinä", +"Aug." => "Elo", +"Sep." => "Syys", +"Oct." => "Loka", +"Nov." => "Marras", +"Dec." => "Joulu", "All day" => "Koko päivä", -"New Calendar" => "Uusi kalenteri", "Missing fields" => "Puuttuvat kentät", "Title" => "Otsikko", "The event ends before it starts" => "Tapahtuma päättyy ennen alkamistaan", @@ -65,9 +87,7 @@ "Month" => "Kuukausi", "List" => "Lista", "Today" => "Tänään", -"Calendars" => "Kalenterit", -"There was a fail, while parsing the file." => "Tiedostoa jäsennettäessä tapahtui virhe.", -"Choose active calendars" => "Valitse aktiiviset kalenterit", +"Settings" => "Asetukset", "Your calendars" => "Omat kalenterisi", "CalDav Link" => "CalDav-linkki", "Shared calendars" => "Jaetut kalenterit", @@ -110,25 +130,26 @@ "Select months" => "Valitse kuukaudet", "Select weeks" => "Valitse viikot", "Interval" => "Intervalli", -"Import a calendar file" => "Tuo kalenteritiedosto", -"Please choose the calendar" => "Valitse kalenteri", "create a new calendar" => "luo uusi kalenteri", +"Import a calendar file" => "Tuo kalenteritiedosto", +"Please choose a calendar" => "Valitse kalenteri", "Name of new calendar" => "Uuden kalenterin nimi", +"A Calendar with this name already exists. If you continue anyhow, these calendars will be merged." => "Samalla nimellä on jo olemassa kalenteri. Jos jatkat kaikesta huolimatta, kalenterit yhdistetään.", "Import" => "Tuo", -"Importing calendar" => "Tuodaan kalenteria", -"Calendar imported successfully" => "Kalenteri tuotu onnistuneesti", "Close Dialog" => "Sulje ikkuna", "Create a new event" => "Luo uusi tapahtuma", "View an event" => "Avaa tapahtuma", "No categories selected" => "Luokkia ei ole valittu", -"Select category" => "Valitse luokka", +"General" => "Yleiset", "Timezone" => "Aikavyöhyke", -"Check always for changes of the timezone" => "Tarkista aina aikavyöhykkeen muutokset", -"Timeformat" => "Ajan esitysmuoto", +"Update timezone automatically" => "Päivitä aikavyöhykkeet automaattisesti", +"Time format" => "Ajan näyttömuoto", "24h" => "24 tuntia", "12h" => "12 tuntia", -"First day of the week" => "Viikon ensimmäinen päivä", -"Calendar CalDAV syncing address:" => "Kalenterin CalDAV-synkronointiosoite:", +"Start week on" => "Viikon alkamispäivä", +"Calendar CalDAV syncing addresses" => "Kalenterin CalDAV-synkronointiosoitteet", +"Primary address (Kontact et al)" => "Ensisijainen osoite (Kontact ja muut vastaavat)", +"iOS/OS X" => "iOS/OS X", "Users" => "Käyttäjät", "select users" => "valitse käyttäjät", "Editable" => "Muoktattava", diff --git a/apps/calendar/l10n/fr.php b/apps/calendar/l10n/fr.php index 506453af42..90ba903b87 100644 --- a/apps/calendar/l10n/fr.php +++ b/apps/calendar/l10n/fr.php @@ -1,12 +1,23 @@ "Tous les calendriers ne sont pas mis en cache", +"Everything seems to be completely cached" => "Tout semble être en cache", "No calendars found." => "Aucun calendrier n'a été trouvé.", "No events found." => "Aucun événement n'a été trouvé.", "Wrong calendar" => "Mauvais calendrier", +"The file contained either no events or all events are already saved in your calendar." => "Soit le fichier ne contient aucun événement soit tous les événements sont déjà enregistrés dans votre calendrier.", +"events has been saved in the new calendar" => "Les événements ont été enregistrés dans le nouveau calendrier", +"Import failed" => "Échec de l'import", +"events has been saved in your calendar" => "Les événements ont été enregistrés dans votre calendrier", "New Timezone:" => "Nouveau fuseau horaire :", "Timezone changed" => "Fuseau horaire modifié", "Invalid request" => "Requête invalide", "Calendar" => "Calendrier", +"ddd" => "ddd", +"ddd M/d" => "ddd d/M", +"dddd M/d" => "dddd d/M", +"MMMM yyyy" => "MMMM yyyy", "MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "MMM d[ yyyy]{ '—'[ MMM] d yyyy}", +"dddd, MMM d, yyyy" => "dddd, d MMM, yyyy", "Birthday" => "Anniversaire", "Business" => "Professionnel", "Call" => "Appel", @@ -22,7 +33,9 @@ "Projects" => "Projets", "Questions" => "Questions", "Work" => "Travail", +"by" => "par", "unnamed" => "sans-nom", +"New Calendar" => "Nouveau Calendrier", "Does not repeat" => "Pas de répétition", "Daily" => "Tous les jours", "Weekly" => "Hebdomadaire", @@ -67,8 +80,26 @@ "by day and month" => "par jour et mois", "Date" => "Date", "Cal." => "Cal.", +"Sun." => "Dim.", +"Mon." => "Lun.", +"Tue." => "Mar.", +"Wed." => "Mer.", +"Thu." => "Jeu", +"Fri." => "Ven.", +"Sat." => "Sam.", +"Jan." => "Jan.", +"Feb." => "Fév.", +"Mar." => "Mars", +"Apr." => "Avr.", +"May." => "Mai", +"Jun." => "Juin", +"Jul." => "Juil.", +"Aug." => "Août", +"Sep." => "Sep.", +"Oct." => "Oct.", +"Nov." => "Nov.", +"Dec." => "Déc.", "All day" => "Journée entière", -"New Calendar" => "Nouveau Calendrier", "Missing fields" => "Champs manquants", "Title" => "Titre", "From Date" => "De la date", @@ -81,9 +112,6 @@ "Month" => "Mois", "List" => "Liste", "Today" => "Aujourd'hui", -"Calendars" => "Calendriers", -"There was a fail, while parsing the file." => "Une erreur est survenue pendant la lecture du fichier.", -"Choose active calendars" => "Choix des calendriers actifs", "Your calendars" => "Vos calendriers", "CalDav Link" => "Lien CalDav", "Shared calendars" => "Calendriers partagés", @@ -132,27 +160,29 @@ "Interval" => "Intervalle", "End" => "Fin", "occurrences" => "occurrences", -"Import a calendar file" => "Importer un fichier de calendriers", -"Please choose the calendar" => "Choisissez le calendrier svp", "create a new calendar" => "Créer un nouveau calendrier", +"Import a calendar file" => "Importer un fichier de calendriers", +"Please choose a calendar" => "Veuillez sélectionner un calendrier", "Name of new calendar" => "Nom pour le nouveau calendrier", +"Take an available name!" => "Choisissez un nom disponible !", +"A Calendar with this name already exists. If you continue anyhow, these calendars will be merged." => "Un calendrier de ce nom existe déjà. Si vous choisissez de continuer les calendriers seront fusionnés.", "Import" => "Importer", -"Importing calendar" => "Import du calendrier", -"Calendar imported successfully" => "Calendrier importé avec succès", "Close Dialog" => "Fermer la fenêtre", "Create a new event" => "Créer un nouvel événement", "View an event" => "Voir un événement", "No categories selected" => "Aucune catégorie sélectionnée", -"Select category" => "Sélectionner une catégorie", "of" => "de", "at" => "à", "Timezone" => "Fuseau horaire", -"Check always for changes of the timezone" => "Toujours vérifier d'éventuels changements de fuseau horaire", -"Timeformat" => "Format de l'heure", "24h" => "24h", "12h" => "12h", -"First day of the week" => "Premier jour de la semaine", -"Calendar CalDAV syncing address:" => "Adresse de synchronisation du calendrier CalDAV :", +"Cache" => "Cache", +"Clear cache for repeating events" => "Nettoyer le cache des événements répétitifs", +"Calendar CalDAV syncing addresses" => "Adresses de synchronisation des calendriers CalDAV", +"more info" => "plus d'infos", +"Primary address (Kontact et al)" => "Adresses principales (Kontact et assimilés)", +"iOS/OS X" => "iOS/OS X", +"Read only iCalendar link(s)" => "lien(s) iCalendar en lecture seule", "Users" => "Utilisateurs", "select users" => "sélectionner les utilisateurs", "Editable" => "Modifiable", diff --git a/apps/calendar/l10n/gl.php b/apps/calendar/l10n/gl.php index 3178b1819e..00a28cc70f 100644 --- a/apps/calendar/l10n/gl.php +++ b/apps/calendar/l10n/gl.php @@ -6,7 +6,12 @@ "Timezone changed" => "Fuso horario trocado", "Invalid request" => "Petición non válida", "Calendar" => "Calendario", +"ddd" => "ddd", +"ddd M/d" => "ddd M/d", +"dddd M/d" => "dddd M/d", +"MMMM yyyy" => "MMMM yyyy", "MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "d MMM[ yyyy]{ '—'d [ MMM] yyyy}", +"dddd, MMM d, yyyy" => "dddd, MMM d,yyyy", "Birthday" => "Aniversario", "Business" => "Traballo", "Call" => "Chamada", @@ -23,6 +28,7 @@ "Questions" => "Preguntas", "Work" => "Traballo", "unnamed" => "sen nome", +"New Calendar" => "Novo calendario", "Does not repeat" => "Non se repite", "Daily" => "A diario", "Weekly" => "Semanalmente", @@ -68,7 +74,6 @@ "Date" => "Data", "Cal." => "Cal.", "All day" => "Todo o dia", -"New Calendar" => "Novo calendario", "Missing fields" => "Faltan campos", "Title" => "Título", "From Date" => "Desde a data", @@ -132,6 +137,7 @@ "Interval" => "Intervalo", "End" => "Fin", "occurrences" => "acontecementos", +"create a new calendar" => "crear un novo calendario", "Import a calendar file" => "Importar un ficheiro de calendario", "Please choose the calendar" => "Por favor, seleccione o calendario", "create a new calendar" => "crear un novo calendario", @@ -143,7 +149,6 @@ "Create a new event" => "Crear un novo evento", "View an event" => "Ver un evento", "No categories selected" => "Non seleccionou as categorías", -"Select category" => "Seleccionar categoría", "of" => "de", "at" => "a", "Timezone" => "Fuso horario", @@ -151,8 +156,6 @@ "Timeformat" => "Formato de hora", "24h" => "24h", "12h" => "12h", -"First day of the week" => "Primeiro día da semana", -"Calendar CalDAV syncing address:" => "Enderezo de sincronización do calendario CalDAV:", "Users" => "Usuarios", "select users" => "escoller usuarios", "Editable" => "Editable", diff --git a/apps/calendar/l10n/he.php b/apps/calendar/l10n/he.php index c161d3be2e..d5c0b2b2e5 100644 --- a/apps/calendar/l10n/he.php +++ b/apps/calendar/l10n/he.php @@ -6,7 +6,12 @@ "Timezone changed" => "אזור זמן השתנה", "Invalid request" => "בקשה לא חוקית", "Calendar" => "ח שנה", +"ddd" => "ddd", +"ddd M/d" => "ddd M/d", +"dddd M/d" => "dddd M/d", +"MMMM yyyy" => "MMMM yyyy", "MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "d MMM [ yyyy]{ '—'d[ MMM] yyyy}", +"dddd, MMM d, yyyy" => "dddd, MMM d, yyyy", "Birthday" => "יום הולדת", "Business" => "עסקים", "Call" => "שיחה", @@ -23,6 +28,7 @@ "Questions" => "שאלות", "Work" => "עבודה", "unnamed" => "ללא שם", +"New Calendar" => "לוח שנה חדש", "Does not repeat" => "ללא חזרה", "Daily" => "יומי", "Weekly" => "שבועי", @@ -68,7 +74,6 @@ "Date" => "תאריך", "Cal." => "לוח שנה", "All day" => "היום", -"New Calendar" => "לוח שנה חדש", "Missing fields" => "שדות חסרים", "Title" => "כותרת", "From Date" => "מתאריך", @@ -81,9 +86,6 @@ "Month" => "חודש", "List" => "רשימה", "Today" => "היום", -"Calendars" => "לוחות שנה", -"There was a fail, while parsing the file." => "אירעה שגיאה בעת פענוח הקובץ.", -"Choose active calendars" => "בחר לוחות שנה פעילים", "Your calendars" => "לוחות השנה שלך", "CalDav Link" => "קישור CalDav", "Shared calendars" => "לוחות שנה מושתפים", @@ -132,27 +134,19 @@ "Interval" => "משך", "End" => "סיום", "occurrences" => "מופעים", -"Import a calendar file" => "יבוא קובץ לוח שנה", -"Please choose the calendar" => "נא לבחור את לוח השנה", "create a new calendar" => "יצירת לוח שנה חדש", +"Import a calendar file" => "יבוא קובץ לוח שנה", "Name of new calendar" => "שם לוח השנה החדש", "Import" => "יבוא", -"Importing calendar" => "היומן מייובא", -"Calendar imported successfully" => "היומן ייובא בהצלחה", "Close Dialog" => "סגירת הדו־שיח", "Create a new event" => "יצירת אירוע חדש", "View an event" => "צפייה באירוע", "No categories selected" => "לא נבחרו קטגוריות", -"Select category" => "בחר קטגוריה", "of" => "מתוך", "at" => "בשנה", "Timezone" => "אזור זמן", -"Check always for changes of the timezone" => "יש לבדוק תמיד אם יש הבדלים באזורי הזמן", -"Timeformat" => "מבנה התאריך", "24h" => "24 שעות", "12h" => "12 שעות", -"First day of the week" => "היום הראשון בשבוע", -"Calendar CalDAV syncing address:" => "כתובת הסנכרון ללוח שנה מסוג CalDAV:", "Users" => "משתמשים", "select users" => "נא לבחור במשתמשים", "Editable" => "ניתן לעריכה", diff --git a/apps/calendar/l10n/hr.php b/apps/calendar/l10n/hr.php index 551bb4abbc..07512b9605 100644 --- a/apps/calendar/l10n/hr.php +++ b/apps/calendar/l10n/hr.php @@ -23,6 +23,7 @@ "Questions" => "Pitanja", "Work" => "Posao", "unnamed" => "bezimeno", +"New Calendar" => "Novi kalendar", "Does not repeat" => "Ne ponavlja se", "Daily" => "Dnevno", "Weekly" => "Tjedno", @@ -67,7 +68,6 @@ "Date" => "datum", "Cal." => "Kal.", "All day" => "Cijeli dan", -"New Calendar" => "Novi kalendar", "Missing fields" => "Nedostaju polja", "Title" => "Naslov", "From Date" => "Datum od", @@ -80,9 +80,6 @@ "Month" => "Mjesec", "List" => "Lista", "Today" => "Danas", -"Calendars" => "Kalendari", -"There was a fail, while parsing the file." => "Pogreška pri čitanju datoteke.", -"Choose active calendars" => "Odabir aktivnih kalendara", "Your calendars" => "Vaši kalendari", "CalDav Link" => "CalDav poveznica", "Shared calendars" => "Podijeljeni kalendari", @@ -128,27 +125,20 @@ "Interval" => "Interval", "End" => "Kraj", "occurrences" => "pojave", -"Import a calendar file" => "Uvozite datoteku kalendara", -"Please choose the calendar" => "Odaberi kalendar", "create a new calendar" => "stvori novi kalendar", +"Import a calendar file" => "Uvozite datoteku kalendara", "Name of new calendar" => "Ime novog kalendara", "Import" => "Uvoz", -"Importing calendar" => "Uvoz kalendara", -"Calendar imported successfully" => "Kalendar je uspješno uvezen", "Close Dialog" => "Zatvori dijalog", "Create a new event" => "Unesi novi događaj", "View an event" => "Vidjeti događaj", "No categories selected" => "Nema odabranih kategorija", -"Select category" => "Odabir kategorije", "of" => "od", "at" => "na", "Timezone" => "Vremenska zona", -"Check always for changes of the timezone" => "Provjerite uvijek za promjene vremenske zone", "Timeformat" => "Format vremena", "24h" => "24h", "12h" => "12h", -"First day of the week" => "Prvi dan tjedna", -"Calendar CalDAV syncing address:" => "Adresa za CalDAV sinkronizaciju kalendara:", "Users" => "Korisnici", "select users" => "odaberi korisnike", "Editable" => "Može se uređivati", diff --git a/apps/calendar/l10n/hu_HU.php b/apps/calendar/l10n/hu_HU.php index d97887aac7..3ef4b9675b 100644 --- a/apps/calendar/l10n/hu_HU.php +++ b/apps/calendar/l10n/hu_HU.php @@ -6,7 +6,12 @@ "Timezone changed" => "Időzóna megváltozott", "Invalid request" => "Érvénytelen kérés", "Calendar" => "Naptár", +"ddd" => "nnn", +"ddd M/d" => "nnn H/n", +"dddd M/d" => "nnnn H/n", +"MMMM yyyy" => "HHHH éééé", "MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "MMM d[ yyyy]{ '—'[ MMM] d yyyy}", +"dddd, MMM d, yyyy" => "nnnn, HHH n, éééé", "Birthday" => "Születésap", "Business" => "Üzlet", "Call" => "Hívás", @@ -23,6 +28,7 @@ "Questions" => "Kérdések", "Work" => "Munka", "unnamed" => "névtelen", +"New Calendar" => "Új naptár", "Does not repeat" => "Nem ismétlődik", "Daily" => "Napi", "Weekly" => "Heti", @@ -68,7 +74,6 @@ "Date" => "Dátum", "Cal." => "Naptár", "All day" => "Egész nap", -"New Calendar" => "Új naptár", "Missing fields" => "Hiányzó mezők", "Title" => "Cím", "From Date" => "Napjától", @@ -81,9 +86,6 @@ "Month" => "Hónap", "List" => "Lista", "Today" => "Ma", -"Calendars" => "Naptárak", -"There was a fail, while parsing the file." => "Probléma volt a fájl elemzése közben.", -"Choose active calendars" => "Aktív naptár kiválasztása", "Your calendars" => "Naptárjaid", "CalDav Link" => "CalDAV link", "Shared calendars" => "Megosztott naptárak", @@ -132,27 +134,19 @@ "Interval" => "Időköz", "End" => "Vége", "occurrences" => "előfordulások", -"Import a calendar file" => "Naptár-fájl importálása", -"Please choose the calendar" => "Válassz naptárat", "create a new calendar" => "új naptár létrehozása", +"Import a calendar file" => "Naptár-fájl importálása", "Name of new calendar" => "Új naptár neve", "Import" => "Importálás", -"Importing calendar" => "Naptár importálása", -"Calendar imported successfully" => "Naptár sikeresen importálva", "Close Dialog" => "Párbeszédablak bezárása", "Create a new event" => "Új esemény létrehozása", "View an event" => "Esemény megtekintése", "No categories selected" => "Nincs kiválasztott kategória", -"Select category" => "Kategória kiválasztása", "of" => ", tulaj ", "at" => ", ", "Timezone" => "Időzóna", -"Check always for changes of the timezone" => "Mindig ellenőrizze az időzóna-változásokat", -"Timeformat" => "Időformátum", "24h" => "24h", "12h" => "12h", -"First day of the week" => "A hét első napja", -"Calendar CalDAV syncing address:" => "Naptár CalDAV szinkronizálási cím:", "Users" => "Felhasználók", "select users" => "válassz felhasználókat", "Editable" => "Szerkeszthető", diff --git a/apps/calendar/l10n/ia.php b/apps/calendar/l10n/ia.php index a346e4de5b..84c36536b9 100644 --- a/apps/calendar/l10n/ia.php +++ b/apps/calendar/l10n/ia.php @@ -16,6 +16,7 @@ "Questions" => "Demandas", "Work" => "Travalio", "unnamed" => "sin nomine", +"New Calendar" => "Nove calendario", "Does not repeat" => "Non repite", "Daily" => "Quotidian", "Weekly" => "Septimanal", @@ -51,7 +52,6 @@ "by day and month" => "per dia e mense", "Date" => "Data", "All day" => "Omne die", -"New Calendar" => "Nove calendario", "Missing fields" => "Campos incomplete", "Title" => "Titulo", "From Date" => "Data de initio", @@ -62,8 +62,6 @@ "Month" => "Mense", "List" => "Lista", "Today" => "Hodie", -"Calendars" => "Calendarios", -"Choose active calendars" => "Selectionar calendarios active", "Your calendars" => "Tu calendarios", "Download" => "Discarga", "Edit" => "Modificar", @@ -96,20 +94,17 @@ "Select weeks" => "Seliger septimanas", "Interval" => "Intervallo", "End" => "Fin", -"Import a calendar file" => "Importar un file de calendario", -"Please choose the calendar" => "Selige el calendario", "create a new calendar" => "crear un nove calendario", +"Import a calendar file" => "Importar un file de calendario", "Name of new calendar" => "Nomine del calendario", "Import" => "Importar", "Close Dialog" => "Clauder dialogo", "Create a new event" => "Crear un nove evento", "View an event" => "Vide un evento", "No categories selected" => "Nulle categorias seligite", -"Select category" => "Selectionar categoria", "of" => "de", "at" => "in", "Timezone" => "Fuso horari", -"First day of the week" => "Prime die del septimana", "Users" => "Usatores", "Groups" => "Gruppos" ); diff --git a/apps/calendar/l10n/id.php b/apps/calendar/l10n/id.php index ac0734abba..865c2118fa 100644 --- a/apps/calendar/l10n/id.php +++ b/apps/calendar/l10n/id.php @@ -14,9 +14,6 @@ "Week" => "Minggu", "Month" => "Bulan", "Today" => "Hari ini", -"Calendars" => "Kalender", -"There was a fail, while parsing the file." => "Terjadi kesalahan, saat mengurai berkas.", -"Choose active calendars" => "Pilih kalender aktif", "Download" => "Unduh", "Edit" => "Sunting", "Edit calendar" => "Sunting kalender", diff --git a/apps/calendar/l10n/it.php b/apps/calendar/l10n/it.php index cdb2d99c82..04e10b582b 100644 --- a/apps/calendar/l10n/it.php +++ b/apps/calendar/l10n/it.php @@ -1,12 +1,23 @@ "Non tutti i calendari sono mantenuti completamente in cache", +"Everything seems to be completely cached" => "Tutto sembra essere mantenuto completamente in cache", "No calendars found." => "Nessun calendario trovato.", "No events found." => "Nessun evento trovato.", "Wrong calendar" => "Calendario sbagliato", +"The file contained either no events or all events are already saved in your calendar." => "Il file non conteneva alcun evento o tutti gli eventi erano già salvati nel tuo calendario.", +"events has been saved in the new calendar" => "gli eventi sono stati salvati nel nuovo calendario", +"Import failed" => "Importazione non riuscita", +"events has been saved in your calendar" => "gli eventi sono stati salvati nel tuo calendario", "New Timezone:" => "Nuovo fuso orario:", "Timezone changed" => "Fuso orario cambiato", "Invalid request" => "Richiesta non valida", "Calendar" => "Calendario", +"ddd" => "ddd", +"ddd M/d" => "ddd d/M", +"dddd M/d" => "dddd d/M", +"MMMM yyyy" => "MMMM yyyy", "MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "MMM d[ yyyy]{ '—'[ MMM] d yyyy}", +"dddd, MMM d, yyyy" => "dddd, d MMM yyyy", "Birthday" => "Compleanno", "Business" => "Azienda", "Call" => "Chiama", @@ -22,7 +33,9 @@ "Projects" => "Progetti", "Questions" => "Domande", "Work" => "Lavoro", +"by" => "da", "unnamed" => "senza nome", +"New Calendar" => "Nuovo calendario", "Does not repeat" => "Non ripetere", "Daily" => "Giornaliero", "Weekly" => "Settimanale", @@ -67,8 +80,26 @@ "by day and month" => "per giorno e mese", "Date" => "Data", "Cal." => "Cal.", +"Sun." => "Dom.", +"Mon." => "Lun.", +"Tue." => "Mar.", +"Wed." => "Mer.", +"Thu." => "Gio.", +"Fri." => "Ven.", +"Sat." => "Sab.", +"Jan." => "Gen.", +"Feb." => "Feb.", +"Mar." => "Mar.", +"Apr." => "Apr.", +"May." => "Mag.", +"Jun." => "Giu.", +"Jul." => "Lug.", +"Aug." => "Ago.", +"Sep." => "Set.", +"Oct." => "Ott.", +"Nov." => "Nov.", +"Dec." => "Dic.", "All day" => "Tutti il giorno", -"New Calendar" => "Nuovo calendario", "Missing fields" => "Campi mancanti", "Title" => "Titolo", "From Date" => "Dal giorno", @@ -81,9 +112,7 @@ "Month" => "Mese", "List" => "Elenco", "Today" => "Oggi", -"Calendars" => "Calendari", -"There was a fail, while parsing the file." => "Si è verificato un errore durante l'analisi del file.", -"Choose active calendars" => "Scegli i calendari attivi", +"Settings" => "Impostazioni", "Your calendars" => "I tuoi calendari", "CalDav Link" => "Collegamento CalDav", "Shared calendars" => "Calendari condivisi", @@ -110,7 +139,7 @@ "Share" => "Condividi", "Title of the Event" => "Titolo dell'evento", "Category" => "Categoria", -"Separate categories with commas" => "Categorie separate con virgole", +"Separate categories with commas" => "Categorie separate da virgole", "Edit categories" => "Modifica le categorie", "All Day Event" => "Evento che occupa tutta la giornata", "From" => "Da", @@ -132,27 +161,34 @@ "Interval" => "Intervallo", "End" => "Fine", "occurrences" => "occorrenze", -"Import a calendar file" => "Importa un file di calendario", -"Please choose the calendar" => "Scegli il calendario", "create a new calendar" => "Crea un nuovo calendario", +"Import a calendar file" => "Importa un file di calendario", +"Please choose a calendar" => "Scegli un calendario", "Name of new calendar" => "Nome del nuovo calendario", +"Take an available name!" => "Usa un nome disponibile!", +"A Calendar with this name already exists. If you continue anyhow, these calendars will be merged." => "Un calendario con questo nome esiste già. Se continui, i due calendari saranno uniti.", "Import" => "Importa", -"Importing calendar" => "Importazione del calendario in corso", -"Calendar imported successfully" => "Calendario importato correttamente", "Close Dialog" => "Chiudi la finestra di dialogo", "Create a new event" => "Crea un nuovo evento", "View an event" => "Visualizza un evento", "No categories selected" => "Nessuna categoria selezionata", -"Select category" => "Seleziona una categoria", "of" => "di", "at" => "alle", +"General" => "Generale", "Timezone" => "Fuso orario", -"Check always for changes of the timezone" => "Controlla sempre i cambiamenti di fuso orario", -"Timeformat" => "Formato orario", +"Update timezone automatically" => "Aggiorna automaticamente il fuso orario", +"Time format" => "Formato orario", "24h" => "24h", "12h" => "12h", -"First day of the week" => "Primo giorno della settimana", -"Calendar CalDAV syncing address:" => "Indirizzo sincronizzazione calendario CalDAV:", +"Start week on" => "La settimana inizia il", +"Cache" => "Cache", +"Clear cache for repeating events" => "Cancella gli eventi che si ripetono dalla cache", +"URLs" => "URL", +"Calendar CalDAV syncing addresses" => "Indirizzi di sincronizzazione calendari CalDAV", +"more info" => "ulteriori informazioni", +"Primary address (Kontact et al)" => "Indirizzo principale (Kontact e altri)", +"iOS/OS X" => "iOS/OS X", +"Read only iCalendar link(s)" => "Collegamento(i) iCalendar sola lettura", "Users" => "Utenti", "select users" => "seleziona utenti", "Editable" => "Modificabile", diff --git a/apps/calendar/l10n/ja_JP.php b/apps/calendar/l10n/ja_JP.php index c533a9bd1a..e59f186a0b 100644 --- a/apps/calendar/l10n/ja_JP.php +++ b/apps/calendar/l10n/ja_JP.php @@ -1,12 +1,23 @@ "すべてのカレンダーは完全にキャッシュされていません", +"Everything seems to be completely cached" => "すべて完全にキャッシュされていると思われます", "No calendars found." => "カレンダーが見つかりませんでした。", "No events found." => "イベントが見つかりませんでした。", "Wrong calendar" => "誤ったカレンダーです", +"The file contained either no events or all events are already saved in your calendar." => "イベントの無いもしくはすべてのイベントを含むファイルは既にあなたのカレンダーに保存されています。", +"events has been saved in the new calendar" => "イベントは新しいカレンダーに保存されました", +"Import failed" => "インポートに失敗", +"events has been saved in your calendar" => "イベントはあなたのカレンダーに保存されました", "New Timezone:" => "新しいタイムゾーン:", "Timezone changed" => "タイムゾーンが変更されました", "Invalid request" => "無効なリクエストです", "Calendar" => "カレンダー", -"MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "MMM d[ yyyy]{ '—'[ MMM] d yyyy}", +"ddd" => "dddd", +"ddd M/d" => "M月d日 (dddd)", +"dddd M/d" => "M月d日 (dddd)", +"MMMM yyyy" => "yyyy年M月", +"MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "yyyy年M月d日{ '~' [yyyy年][M月]d日}", +"dddd, MMM d, yyyy" => "yyyy年M月d日 (dddd)", "Birthday" => "誕生日", "Business" => "ビジネス", "Call" => "電話をかける", @@ -22,6 +33,9 @@ "Projects" => "プロジェクト", "Questions" => "質問事項", "Work" => "週の始まり", +"by" => "による", +"unnamed" => "無名", +"New Calendar" => "新しくカレンダーを作成", "Does not repeat" => "繰り返さない", "Daily" => "毎日", "Weekly" => "毎週", @@ -34,13 +48,13 @@ "by date" => "日付で指定", "by monthday" => "日にちで指定", "by weekday" => "曜日で指定", -"Monday" => "月曜", -"Tuesday" => "火曜", -"Wednesday" => "水曜", -"Thursday" => "木曜", -"Friday" => "金曜", -"Saturday" => "土曜", -"Sunday" => "日曜", +"Monday" => "月", +"Tuesday" => "火", +"Wednesday" => "水", +"Thursday" => "木", +"Friday" => "金", +"Saturday" => "土", +"Sunday" => "日", "events week of month" => "予定のある週を指定", "first" => "1週目", "second" => "2週目", @@ -48,26 +62,45 @@ "fourth" => "4週目", "fifth" => "5週目", "last" => "最終週", -"January" => "1月", -"February" => "2月", -"March" => "3月", -"April" => "4月", -"May" => "5月", -"June" => "6月", -"July" => "7月", -"August" => "8月", -"September" => "9月", -"October" => "10月", -"November" => "11月", -"December" => "12月", +"January" => "1月", +"February" => "2月", +"March" => "3月", +"April" => "4月", +"May" => "5月", +"June" => "6月", +"July" => "7月", +"August" => "8月", +"September" => "9月", +"October" => "10月", +"November" => "11月", +"December" => "12月", "by events date" => "日付で指定", "by yearday(s)" => "日番号で指定", "by weeknumber(s)" => "週番号で指定", "by day and month" => "月と日で指定", "Date" => "日付", "Cal." => "カレンダー", +"Sun." => "日", +"Mon." => "月", +"Tue." => "火", +"Wed." => "水", +"Thu." => "木", +"Fri." => "金", +"Sat." => "土", +"Jan." => "1月", +"Feb." => "2月", +"Mar." => "3月", +"Apr." => "4月", +"May." => "5月", +"Jun." => "6月", +"Jul." => "7月", +"Aug." => "8月", +"Sep." => "9月", +"Oct." => "10月", +"Nov." => "11月", +"Dec." => "12月", "All day" => "終日", -"New Calendar" => "新しくカレンダーを作成", +"New Calendar" => "新しくカレンダーを作成する", "Missing fields" => "項目がありません", "Title" => "タイトル", "From Date" => "開始日", @@ -78,11 +111,9 @@ "There was a database fail" => "データベースのエラーがありました", "Week" => "週", "Month" => "月", -"List" => "リスト", +"List" => "予定リスト", "Today" => "今日", -"Calendars" => "カレンダー", -"There was a fail, while parsing the file." => "ファイルの構文解析に失敗しました。", -"Choose active calendars" => "アクティブなカレンダーを選択", +"Settings" => "設定", "Your calendars" => "あなたのカレンダー", "CalDav Link" => "CalDavへのリンク", "Shared calendars" => "共有カレンダー", @@ -91,6 +122,7 @@ "Download" => "ダウンロード", "Edit" => "編集", "Delete" => "削除", +"shared with you by" => "共有者", "New calendar" => "新しいカレンダー", "Edit calendar" => "カレンダーを編集", "Displayname" => "表示名", @@ -130,27 +162,34 @@ "Interval" => "間隔", "End" => "繰り返す期間", "occurrences" => "回繰り返す", -"Import a calendar file" => "カレンダーファイルをインポート", -"Please choose the calendar" => "カレンダーを選択してください", "create a new calendar" => "新規カレンダーの作成", +"Import a calendar file" => "カレンダーファイルをインポート", +"Please choose a calendar" => "カレンダーを選択してください", "Name of new calendar" => "新規カレンダーの名称", +"Take an available name!" => "利用可能な名前を指定してください!", +"A Calendar with this name already exists. If you continue anyhow, these calendars will be merged." => "このカレンダー名はすでに使われています。もし続行する場合は、これらのカレンダーはマージされます。", "Import" => "インポート", -"Importing calendar" => "カレンダーを取り込み中", -"Calendar imported successfully" => "カレンダーの取り込みに成功しました", "Close Dialog" => "ダイアログを閉じる", "Create a new event" => "新しいイベントを作成", "View an event" => "イベントを閲覧", "No categories selected" => "カテゴリが選択されていません", -"Select category" => "カテゴリーを選択してください", "of" => "of", "at" => "at", +"General" => "一般", "Timezone" => "タイムゾーン", -"Check always for changes of the timezone" => "タイムゾーンの変更を常に確認", -"Timeformat" => "時刻のフォーマット", +"Update timezone automatically" => "自動的にタイムゾーンを更新", +"Time format" => "時刻の表示形式", "24h" => "24h", "12h" => "12h", -"First day of the week" => "週の始まり", -"Calendar CalDAV syncing address:" => "CalDAVカレンダーの同期アドレス:", +"Start week on" => "1週間の初めの曜日", +"Cache" => "キャッシュ", +"Clear cache for repeating events" => "繰り返しイベントのキャッシュをクリア", +"URLs" => "URL", +"Calendar CalDAV syncing addresses" => "CalDAVカレンダーの同期用アドレス", +"more info" => "さらに", +"Primary address (Kontact et al)" => "プライマリアドレス(コンタクト等)", +"iOS/OS X" => "iOS/OS X", +"Read only iCalendar link(s)" => "読み取り専用のiCalendarリンク", "Users" => "ユーザ", "select users" => "ユーザを選択", "Editable" => "編集可能", diff --git a/apps/calendar/l10n/ko.php b/apps/calendar/l10n/ko.php index 181bfa4378..77e421d4aa 100644 --- a/apps/calendar/l10n/ko.php +++ b/apps/calendar/l10n/ko.php @@ -6,6 +6,12 @@ "Timezone changed" => "시간대 변경됨", "Invalid request" => "잘못된 요청", "Calendar" => "달력", +"ddd" => "ddd", +"ddd M/d" => "ddd M/d", +"dddd M/d" => "dddd M/d", +"MMMM yyyy" => "MMMM yyyy", +"MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "MMM d[ yyyy]{ '—'[ MMM] d yyyy}", +"dddd, MMM d, yyyy" => "dddd, MMM d, yyyy", "Birthday" => "생일", "Business" => "사업", "Call" => "통화", @@ -22,6 +28,7 @@ "Questions" => "질문", "Work" => "작업", "unnamed" => "익명의", +"New Calendar" => "새로운 달력", "Does not repeat" => "반복 없음", "Daily" => "매일", "Weekly" => "매주", @@ -67,7 +74,6 @@ "Date" => "날짜", "Cal." => "달력", "All day" => "매일", -"New Calendar" => "새로운 달력", "Missing fields" => "기입란이 비어있습니다", "Title" => "제목", "From Date" => "시작날짜", @@ -80,9 +86,6 @@ "Month" => "달", "List" => "목록", "Today" => "오늘", -"Calendars" => "달력", -"There was a fail, while parsing the file." => "파일을 처리하는 중 오류가 발생하였습니다.", -"Choose active calendars" => "활성 달력 선택", "Your calendars" => "내 달력", "CalDav Link" => "CalDav 링크", "Shared calendars" => "공유 달력", @@ -131,27 +134,19 @@ "Interval" => "간격", "End" => "끝", "occurrences" => "번 이후", -"Import a calendar file" => "달력 파일 가져오기", -"Please choose the calendar" => "달력을 선택해 주세요", "create a new calendar" => "새 달력 만들기", +"Import a calendar file" => "달력 파일 가져오기", "Name of new calendar" => "새 달력 이름", "Import" => "입력", -"Importing calendar" => "달력 입력", -"Calendar imported successfully" => "달력 입력을 성공적으로 마쳤습니다.", "Close Dialog" => "대화 마침", "Create a new event" => "새 이벤트 만들기", "View an event" => "일정 보기", "No categories selected" => "선택된 카테고리 없음", -"Select category" => "선택 카테고리", "of" => "의", "at" => "에서", "Timezone" => "시간대", -"Check always for changes of the timezone" => "항상 시간대 변경 확인하기", -"Timeformat" => "시간 형식 설정", "24h" => "24시간", "12h" => "12시간", -"First day of the week" => "그 주의 첫째날", -"Calendar CalDAV syncing address:" => "달력 CalDav 동기화 주소", "Users" => "사용자", "select users" => "사용자 선택", "Editable" => "편집 가능", diff --git a/apps/calendar/l10n/lb.php b/apps/calendar/l10n/lb.php index b40f652cc5..6e96b5df18 100644 --- a/apps/calendar/l10n/lb.php +++ b/apps/calendar/l10n/lb.php @@ -22,6 +22,7 @@ "Projects" => "Projeten", "Questions" => "Froen", "Work" => "Aarbecht", +"New Calendar" => "Neien Kalenner", "Does not repeat" => "Widderhëlt sech net", "Daily" => "Deeglech", "Weekly" => "All Woch", @@ -62,7 +63,6 @@ "Date" => "Datum", "Cal." => "Cal.", "All day" => "All Dag", -"New Calendar" => "Neien Kalenner", "Missing fields" => "Felder déi feelen", "Title" => "Titel", "From Date" => "Vun Datum", @@ -75,9 +75,6 @@ "Month" => "Mount", "List" => "Lescht", "Today" => "Haut", -"Calendars" => "Kalenneren", -"There was a fail, while parsing the file." => "Feeler beim lueden vum Fichier.", -"Choose active calendars" => "Wiel aktiv Kalenneren aus", "Your calendars" => "Deng Kalenneren", "CalDav Link" => "CalDav Link", "Shared calendars" => "Gedeelte Kalenneren", diff --git a/apps/calendar/l10n/lt_LT.php b/apps/calendar/l10n/lt_LT.php index d7e15fb438..408718071e 100644 --- a/apps/calendar/l10n/lt_LT.php +++ b/apps/calendar/l10n/lt_LT.php @@ -23,6 +23,7 @@ "Questions" => "Klausimai", "Work" => "Darbas", "unnamed" => "be pavadinimo", +"New Calendar" => "Naujas kalendorius", "Does not repeat" => "Nekartoti", "Daily" => "Kasdien", "Weekly" => "Kiekvieną savaitę", @@ -56,7 +57,6 @@ "Date" => "Data", "Cal." => "Kal.", "All day" => "Visa diena", -"New Calendar" => "Naujas kalendorius", "Missing fields" => "Trūkstami laukai", "Title" => "Pavadinimas", "From Date" => "Nuo datos", @@ -69,9 +69,6 @@ "Month" => "Mėnuo", "List" => "Sąrašas", "Today" => "Šiandien", -"Calendars" => "Kalendoriai", -"There was a fail, while parsing the file." => "Apdorojant failą įvyko klaida.", -"Choose active calendars" => "Pasirinkite naudojamus kalendorius", "Your calendars" => "Jūsų kalendoriai", "CalDav Link" => "CalDav adresas", "Shared calendars" => "Bendri kalendoriai", @@ -92,6 +89,7 @@ "Export" => "Eksportuoti", "Eventinfo" => "Informacija", "Repeating" => "Pasikartojantis", +"Alarm" => "Priminimas", "Attendees" => "Dalyviai", "Share" => "Dalintis", "Title of the Event" => "Įvykio pavadinimas", @@ -113,6 +111,7 @@ "Select weeks" => "Pasirinkite savaites", "Interval" => "Intervalas", "End" => "Pabaiga", +"create a new calendar" => "sukurti naują kalendorių", "Import a calendar file" => "Importuoti kalendoriaus failą", "Please choose the calendar" => "Pasirinkite kalendorių", "create a new calendar" => "sukurti naują kalendorių", @@ -124,13 +123,11 @@ "Create a new event" => "Sukurti naują įvykį", "View an event" => "Peržiūrėti įvykį", "No categories selected" => "Nepasirinktos jokios katagorijos", -"Select category" => "Pasirinkite kategoriją", "Timezone" => "Laiko juosta", "Check always for changes of the timezone" => "Visada tikrinti laiko zonos pasikeitimus", "Timeformat" => "Laiko formatas", "24h" => "24val", "12h" => "12val", -"Calendar CalDAV syncing address:" => "CalDAV kalendoriaus synchronizavimo adresas:", "Users" => "Vartotojai", "select users" => "pasirinkti vartotojus", "Editable" => "Redaguojamas", diff --git a/apps/calendar/l10n/mk.php b/apps/calendar/l10n/mk.php index 41df376dfa..1a03101fe5 100644 --- a/apps/calendar/l10n/mk.php +++ b/apps/calendar/l10n/mk.php @@ -6,7 +6,12 @@ "Timezone changed" => "Временската зона е променета", "Invalid request" => "Неправилно барање", "Calendar" => "Календар", +"ddd" => "ддд", +"ddd M/d" => "ддд М/д", +"dddd M/d" => "дддд М/д", +"MMMM yyyy" => "ММММ гггг", "MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "MMM d[ yyyy]{ '—'[ MMM] d yyyy}", +"dddd, MMM d, yyyy" => "дддд, МММ д, гггг", "Birthday" => "Роденден", "Business" => "Деловно", "Call" => "Повикај", @@ -23,6 +28,7 @@ "Questions" => "Прашања", "Work" => "Работа", "unnamed" => "неименувано", +"New Calendar" => "Нов календар", "Does not repeat" => "Не се повторува", "Daily" => "Дневно", "Weekly" => "Седмично", @@ -68,7 +74,6 @@ "Date" => "Датум", "Cal." => "Кал.", "All day" => "Цел ден", -"New Calendar" => "Нов календар", "Missing fields" => "Полиња кои недостасуваат", "Title" => "Наслов", "From Date" => "Од датум", @@ -81,9 +86,6 @@ "Month" => "Месец", "List" => "Листа", "Today" => "Денеска", -"Calendars" => "Календари", -"There was a fail, while parsing the file." => "Имаше проблем при парсирање на датотеката.", -"Choose active calendars" => "Избери активни календари", "Your calendars" => "Ваши календари", "CalDav Link" => "Врска за CalDav", "Shared calendars" => "Споделени календари", @@ -132,27 +134,19 @@ "Interval" => "интервал", "End" => "Крај", "occurrences" => "повторувања", -"Import a calendar file" => "Внеси календар од датотека ", -"Please choose the calendar" => "Ве молам изберете го календарот", "create a new calendar" => "создади нов календар", +"Import a calendar file" => "Внеси календар од датотека ", "Name of new calendar" => "Име на новиот календар", "Import" => "Увези", -"Importing calendar" => "Увезување на календар", -"Calendar imported successfully" => "Календарот беше успешно увезен", "Close Dialog" => "Затвори дијалог", "Create a new event" => "Создади нов настан", "View an event" => "Погледај настан", "No categories selected" => "Нема избрано категории", -"Select category" => "Избери категорија", "of" => "од", "at" => "на", "Timezone" => "Временска зона", -"Check always for changes of the timezone" => "Секогаш провери за промени на временската зона", -"Timeformat" => "Формат на времето", "24h" => "24ч", "12h" => "12ч", -"First day of the week" => "Прв ден од седмицата", -"Calendar CalDAV syncing address:" => "CalDAV календар адресата за синхронизација:", "Users" => "Корисници", "select users" => "избери корисници", "Editable" => "Изменливо", diff --git a/apps/calendar/l10n/ms_MY.php b/apps/calendar/l10n/ms_MY.php index 2cb3ac41c3..4be91a4019 100644 --- a/apps/calendar/l10n/ms_MY.php +++ b/apps/calendar/l10n/ms_MY.php @@ -1,9 +1,17 @@ "Tiada kalendar dijumpai.", +"No events found." => "Tiada agenda dijumpai.", "Wrong calendar" => "Silap kalendar", "New Timezone:" => "Timezone Baru", "Timezone changed" => "Zon waktu diubah", "Invalid request" => "Permintaan tidak sah", "Calendar" => "Kalendar", +"ddd" => "ddd", +"ddd M/d" => "dd M/d", +"dddd M/d" => "dddd M/d", +"MMMM yyyy" => "MMMM yyyy", +"MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "MMM d[ yyyy]{ '—'[ MMM] d yyyy}", +"dddd, MMM d, yyyy" => "dddd, MMM d, yyy", "Birthday" => "Hari lahir", "Business" => "Perniagaan", "Call" => "Panggilan", @@ -19,6 +27,8 @@ "Projects" => "Projek", "Questions" => "Soalan", "Work" => "Kerja", +"unnamed" => "tiada nama", +"New Calendar" => "Kalendar baru", "Does not repeat" => "Tidak berulang", "Daily" => "Harian", "Weekly" => "Mingguan", @@ -64,7 +74,6 @@ "Date" => "Tarikh", "Cal." => "Kalendar", "All day" => "Sepanjang hari", -"New Calendar" => "Kalendar baru", "Missing fields" => "Ruangan tertinggal", "Title" => "Tajuk", "From Date" => "Dari tarikh", @@ -77,13 +86,15 @@ "Month" => "Bulan", "List" => "Senarai", "Today" => "Hari ini", -"Calendars" => "Kalendar", -"There was a fail, while parsing the file." => "Berlaku kegagalan ketika penguraian fail. ", -"Choose active calendars" => "Pilih kalendar yang aktif", +"Your calendars" => "Kalendar anda", "CalDav Link" => "Pautan CalDav", +"Shared calendars" => "Kalendar Kongsian", +"No shared calendars" => "Tiada kalendar kongsian", +"Share Calendar" => "Kongsi Kalendar", "Download" => "Muat turun", "Edit" => "Edit", "Delete" => "Hapus", +"shared with you by" => "dikongsi dengan kamu oleh", "New calendar" => "Kalendar baru", "Edit calendar" => "Edit kalendar", "Displayname" => "Paparan nama", @@ -94,8 +105,15 @@ "Cancel" => "Batal", "Edit an event" => "Edit agenda", "Export" => "Export", +"Eventinfo" => "Maklumat agenda", +"Repeating" => "Pengulangan", +"Alarm" => "Penggera", +"Attendees" => "Jemputan", +"Share" => "Berkongsi", "Title of the Event" => "Tajuk agenda", "Category" => "kategori", +"Separate categories with commas" => "Asingkan kategori dengan koma", +"Edit categories" => "Sunting Kategori", "All Day Event" => "Agenda di sepanjang hari ", "From" => "Dari", "To" => "ke", @@ -116,20 +134,23 @@ "Interval" => "Tempoh", "End" => "Tamat", "occurrences" => "Peristiwa", -"Import a calendar file" => "Import fail kalendar", -"Please choose the calendar" => "Sila pilih kalendar", "create a new calendar" => "Cipta kalendar baru", +"Import a calendar file" => "Import fail kalendar", "Name of new calendar" => "Nama kalendar baru", "Import" => "Import", -"Importing calendar" => "Import kalendar", -"Calendar imported successfully" => "Kalendar berjaya diimport", "Close Dialog" => "Tutup dialog", "Create a new event" => "Buat agenda baru", -"Select category" => "Pilih kategori", +"View an event" => "Papar peristiwa", +"No categories selected" => "Tiada kategori dipilih", +"of" => "dari", +"at" => "di", "Timezone" => "Zon waktu", -"Check always for changes of the timezone" => "Sentiasa mengemaskini perubahan zon masa", -"Timeformat" => "Timeformat", "24h" => "24h", "12h" => "12h", -"Calendar CalDAV syncing address:" => "Kelendar CalDAV mengemaskini alamat:" +"Users" => "Pengguna", +"select users" => "Pilih pengguna", +"Editable" => "Boleh disunting", +"Groups" => "Kumpulan-kumpulan", +"select groups" => "pilih kumpulan-kumpulan", +"make public" => "jadikan tontonan awam" ); diff --git a/apps/calendar/l10n/nb_NO.php b/apps/calendar/l10n/nb_NO.php index 95ba5a9dba..8f736869de 100644 --- a/apps/calendar/l10n/nb_NO.php +++ b/apps/calendar/l10n/nb_NO.php @@ -8,6 +8,7 @@ "Calendar" => "Kalender", "Birthday" => "Bursdag", "Business" => "Forretninger", +"Call" => "Ring", "Clients" => "Kunder", "Holidays" => "Ferie", "Ideas" => "Ideér", @@ -20,6 +21,7 @@ "Questions" => "Spørsmål", "Work" => "Arbeid", "unnamed" => "uten navn", +"New Calendar" => "Ny kalender", "Does not repeat" => "Gjentas ikke", "Daily" => "Daglig", "Weekly" => "Ukentlig", @@ -129,6 +131,7 @@ "Interval" => "Intervall", "End" => "Slutt", "occurrences" => "forekomster", +"create a new calendar" => "Lag en ny kalender", "Import a calendar file" => "Importer en kalenderfil", "Please choose the calendar" => "Vennligst velg kalenderen", "create a new calendar" => "Lag en ny kalender", @@ -147,7 +150,7 @@ "24h" => "24 t", "12h" => "12 t", "First day of the week" => "Ukens første dag", -"Calendar CalDAV syncing address:" => "Synkroniseringsadresse fo kalender CalDAV:", +"Calendar CalDAV syncing address:" => "Kalender CalDAV synkroniseringsadresse", "Users" => "Brukere", "select users" => "valgte brukere", "Editable" => "Redigerbar", diff --git a/apps/calendar/l10n/nl.php b/apps/calendar/l10n/nl.php index d141a1cc08..834c0ad905 100644 --- a/apps/calendar/l10n/nl.php +++ b/apps/calendar/l10n/nl.php @@ -6,7 +6,12 @@ "Timezone changed" => "Tijdzone is veranderd", "Invalid request" => "Ongeldige aanvraag", "Calendar" => "Kalender", -"MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "d MMM[ yyyy]{ '—' d[ MMM] yyyy}", +"ddd" => "ddd", +"ddd M/d" => "ddd d.M", +"dddd M/d" => "dddd d.M", +"MMMM yyyy" => "MMMM yyyy", +"MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "d[ MMM][ yyyy]{ '—' d MMM yyyy}", +"dddd, MMM d, yyyy" => "dddd, d. MMM yyyy", "Birthday" => "Verjaardag", "Business" => "Zakelijk", "Call" => "Bellen", @@ -23,6 +28,7 @@ "Questions" => "Vragen", "Work" => "Werk", "unnamed" => "onbekend", +"New Calendar" => "Nieuwe Kalender", "Does not repeat" => "Wordt niet herhaald", "Daily" => "Dagelijks", "Weekly" => "Wekelijks", @@ -68,7 +74,6 @@ "Date" => "Datum", "Cal." => "Cal.", "All day" => "Hele dag", -"New Calendar" => "Nieuwe Kalender", "Missing fields" => "missende velden", "Title" => "Titel", "From Date" => "Begindatum", @@ -81,9 +86,6 @@ "Month" => "Maand", "List" => "Lijst", "Today" => "Vandaag", -"Calendars" => "Kalenders", -"There was a fail, while parsing the file." => "Er is een fout opgetreden bij het verwerken van het bestand.", -"Choose active calendars" => "Kies actieve kalenders", "Your calendars" => "Je kalenders", "CalDav Link" => "CalDav Link", "Shared calendars" => "Gedeelde kalenders", @@ -132,27 +134,19 @@ "Interval" => "Interval", "End" => "Einde", "occurrences" => "gebeurtenissen", -"Import a calendar file" => "Importeer een agenda bestand", -"Please choose the calendar" => "Kies de kalender", "create a new calendar" => "Maak een nieuw agenda", +"Import a calendar file" => "Importeer een agenda bestand", "Name of new calendar" => "Naam van de nieuwe agenda", "Import" => "Importeer", -"Importing calendar" => "Importeer agenda", -"Calendar imported successfully" => "Agenda succesvol geïmporteerd", "Close Dialog" => "Sluit venster", "Create a new event" => "Maak een nieuwe afspraak", "View an event" => "Bekijk een gebeurtenis", "No categories selected" => "Geen categorieën geselecteerd", -"Select category" => "Kies een categorie", "of" => "van", "at" => "op", "Timezone" => "Tijdzone", -"Check always for changes of the timezone" => "Controleer altijd op aanpassingen van de tijdszone", -"Timeformat" => "Tijdformaat", "24h" => "24uur", "12h" => "12uur", -"First day of the week" => "Eerste dag van de week", -"Calendar CalDAV syncing address:" => "CalDAV kalender synchronisatie adres:", "Users" => "Gebruikers", "select users" => "kies gebruikers", "Editable" => "Te wijzigen", diff --git a/apps/calendar/l10n/nn_NO.php b/apps/calendar/l10n/nn_NO.php index 79119e8100..3330cc562b 100644 --- a/apps/calendar/l10n/nn_NO.php +++ b/apps/calendar/l10n/nn_NO.php @@ -19,6 +19,7 @@ "Projects" => "Prosjekt", "Questions" => "Spørsmål", "Work" => "Arbeid", +"New Calendar" => "Ny kalender", "Does not repeat" => "Ikkje gjenta", "Daily" => "Kvar dag", "Weekly" => "Kvar veke", @@ -64,7 +65,6 @@ "Date" => "Dato", "Cal." => "Kal.", "All day" => "Heile dagen", -"New Calendar" => "Ny kalender", "Missing fields" => "Manglande felt", "Title" => "Tittel", "From Date" => "Frå dato", @@ -77,9 +77,6 @@ "Month" => "Månad", "List" => "Liste", "Today" => "I dag", -"Calendars" => "Kalendarar", -"There was a fail, while parsing the file." => "Feil ved tolking av fila.", -"Choose active calendars" => "Vel aktive kalendarar", "CalDav Link" => "CalDav-lenkje", "Download" => "Last ned", "Edit" => "Endra", @@ -116,20 +113,13 @@ "Interval" => "Intervall", "End" => "Ende", "occurrences" => "førekomstar", -"Import a calendar file" => "Importer ei kalenderfil", -"Please choose the calendar" => "Venlegast vel kalenderen", "create a new calendar" => "Lag ny kalender", +"Import a calendar file" => "Importer ei kalenderfil", "Name of new calendar" => "Namn for ny kalender", "Import" => "Importer", -"Importing calendar" => "Importerar kalender", -"Calendar imported successfully" => "Kalender importert utan feil", "Close Dialog" => "Steng dialog", "Create a new event" => "Opprett ei ny hending", -"Select category" => "Vel kategori", "Timezone" => "Tidssone", -"Check always for changes of the timezone" => "Sjekk alltid for endringar i tidssona", -"Timeformat" => "Tidsformat", "24h" => "24t", -"12h" => "12t", -"Calendar CalDAV syncing address:" => "Kalender CalDAV synkroniseringsadresse:" +"12h" => "12t" ); diff --git a/apps/calendar/l10n/pl.php b/apps/calendar/l10n/pl.php index e582cdbb9b..0174bef6fc 100644 --- a/apps/calendar/l10n/pl.php +++ b/apps/calendar/l10n/pl.php @@ -2,11 +2,18 @@ "No calendars found." => "Brak kalendarzy", "No events found." => "Brak wydzarzeń", "Wrong calendar" => "Nieprawidłowy kalendarz", +"Import failed" => "Import nieudany", +"events has been saved in your calendar" => "zdarzenie zostało zapisane w twoim kalendarzu", "New Timezone:" => "Nowa strefa czasowa:", "Timezone changed" => "Zmieniono strefę czasową", "Invalid request" => "Nieprawidłowe żądanie", "Calendar" => "Kalendarz", +"ddd" => "ddd", +"ddd M/d" => "ddd M/d", +"dddd M/d" => "dddd M/d", +"MMMM yyyy" => "MMMM rrrr", "MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "MMM d[ yyyy]{ '—'[ MMM] d yyyy}", +"dddd, MMM d, yyyy" => "dddd, MMM d, rrrr", "Birthday" => "Urodziny", "Business" => "Interesy", "Call" => "Rozmowy", @@ -22,7 +29,9 @@ "Projects" => "Projekty", "Questions" => "Pytania", "Work" => "Zawodowe", +"by" => "przez", "unnamed" => "nienazwany", +"New Calendar" => "Nowy kalendarz", "Does not repeat" => "Brak", "Daily" => "Codziennie", "Weekly" => "Tygodniowo", @@ -67,8 +76,26 @@ "by day and month" => "przez dzień i miesiąc", "Date" => "Data", "Cal." => "Kal.", +"Sun." => "N.", +"Mon." => "Pn.", +"Tue." => "Wt.", +"Wed." => "Śr.", +"Thu." => "Cz.", +"Fri." => "Pt.", +"Sat." => "S.", +"Jan." => "Sty.", +"Feb." => "Lut.", +"Mar." => "Mar.", +"Apr." => "Kwi.", +"May." => "Maj.", +"Jun." => "Cze.", +"Jul." => "Lip.", +"Aug." => "Sie.", +"Sep." => "Wrz.", +"Oct." => "Paź.", +"Nov." => "Lis.", +"Dec." => "Gru.", "All day" => "Cały dzień", -"New Calendar" => "Nowy kalendarz", "Missing fields" => "Brakujące pola", "Title" => "Nazwa", "From Date" => "Od daty", @@ -81,9 +108,6 @@ "Month" => "Miesiąc", "List" => "Lista", "Today" => "Dzisiaj", -"Calendars" => "Kalendarze", -"There was a fail, while parsing the file." => "Nie udało się przetworzyć pliku.", -"Choose active calendars" => "Wybór aktywnych kalendarzy", "Your calendars" => "Twoje kalendarze", "CalDav Link" => "Wyświetla odnośnik CalDAV", "Shared calendars" => "Współdzielone kalendarze", @@ -132,9 +156,9 @@ "Interval" => "Interwał", "End" => "Koniec", "occurrences" => "wystąpienia", -"Import a calendar file" => "Zaimportuj plik kalendarza", -"Please choose the calendar" => "Proszę wybrać kalendarz", "create a new calendar" => "stwórz nowy kalendarz", +"Import a calendar file" => "Zaimportuj plik kalendarza", +"Please choose a calendar" => "Proszę wybierz kalendarz", "Name of new calendar" => "Nazwa kalendarza", "Import" => "Import", "Importing calendar" => "Importuje kalendarz", @@ -143,7 +167,6 @@ "Create a new event" => "Tworzenie nowego wydarzenia", "View an event" => "Zobacz wydarzenie", "No categories selected" => "nie zaznaczono kategorii", -"Select category" => "Wybierz kategorię", "of" => "z", "at" => "w", "Timezone" => "Strefa czasowa", @@ -151,8 +174,9 @@ "Timeformat" => "Format czasu", "24h" => "24h", "12h" => "12h", -"First day of the week" => "Pierwszy dzień tygodnia", -"Calendar CalDAV syncing address:" => "Adres synchronizacji kalendarza CalDAV:", +"more info" => "więcej informacji", +"iOS/OS X" => "iOS/OS X", +"Read only iCalendar link(s)" => "Odczytać tylko linki iCalendar", "Users" => "Użytkownicy", "select users" => "wybierz użytkowników", "Editable" => "Edytowalne", diff --git a/apps/calendar/l10n/pt_BR.php b/apps/calendar/l10n/pt_BR.php index 95317eea0f..b636c19bfe 100644 --- a/apps/calendar/l10n/pt_BR.php +++ b/apps/calendar/l10n/pt_BR.php @@ -6,7 +6,12 @@ "Timezone changed" => "Fuso horário alterado", "Invalid request" => "Pedido inválido", "Calendar" => "Calendário", +"ddd" => "ddd", +"ddd M/d" => "ddd M/d", +"dddd M/d" => "dddd M/d", +"MMMM yyyy" => "MMMM yyyy", "MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "MMM d[ yyyy]{ '—'[ MMM] d yyyy}", +"dddd, MMM d, yyyy" => "dddd, MMM d, yyyy", "Birthday" => "Aniversário", "Business" => "Negócio", "Call" => "Chamada", @@ -23,6 +28,7 @@ "Questions" => "Perguntas", "Work" => "Trabalho", "unnamed" => "sem nome", +"New Calendar" => "Novo Calendário", "Does not repeat" => "Não repetir", "Daily" => "Diariamente", "Weekly" => "Semanal", @@ -68,7 +74,6 @@ "Date" => "Data", "Cal." => "Cal.", "All day" => "Todo o dia", -"New Calendar" => "Novo Calendário", "Missing fields" => "Campos incompletos", "Title" => "Título", "From Date" => "Desde a Data", @@ -81,9 +86,6 @@ "Month" => "Mês", "List" => "Lista", "Today" => "Hoje", -"Calendars" => "Calendários", -"There was a fail, while parsing the file." => "Houve uma falha, ao analisar o arquivo.", -"Choose active calendars" => "Escolha calendários ativos", "Your calendars" => "Meus Calendários", "CalDav Link" => "Link para CalDav", "Shared calendars" => "Calendários Compartilhados", @@ -132,27 +134,19 @@ "Interval" => "Intervalo", "End" => "Final", "occurrences" => "ocorrências", -"Import a calendar file" => "Importar um arquivo de calendário", -"Please choose the calendar" => "Por favor, escolha o calendário", "create a new calendar" => "criar um novo calendário", +"Import a calendar file" => "Importar um arquivo de calendário", "Name of new calendar" => "Nome do novo calendário", "Import" => "Importar", -"Importing calendar" => "Importar calendário", -"Calendar imported successfully" => "Calendário importado com sucesso", "Close Dialog" => "Fechar caixa de diálogo", "Create a new event" => "Criar um novo evento", "View an event" => "Visualizar evento", "No categories selected" => "Nenhuma categoria selecionada", -"Select category" => "Selecionar categoria", "of" => "de", "at" => "para", "Timezone" => "Fuso horário", -"Check always for changes of the timezone" => "Verificar sempre mudanças no fuso horário", -"Timeformat" => "Formato da Hora", "24h" => "24h", "12h" => "12h", -"First day of the week" => "Primeiro dia da semana", -"Calendar CalDAV syncing address:" => "Sincronização de endereço do calendário CalDAV :", "Users" => "Usuários", "select users" => "Selecione usuários", "Editable" => "Editável", diff --git a/apps/calendar/l10n/pt_PT.php b/apps/calendar/l10n/pt_PT.php index 33f85569cc..81bab52e59 100644 --- a/apps/calendar/l10n/pt_PT.php +++ b/apps/calendar/l10n/pt_PT.php @@ -1,12 +1,23 @@ "Nem todos os calendários estão completamente pré-carregados", +"Everything seems to be completely cached" => "Parece que tudo está completamente pré-carregado", "No calendars found." => "Nenhum calendário encontrado.", "No events found." => "Nenhum evento encontrado.", "Wrong calendar" => "Calendário errado", +"The file contained either no events or all events are already saved in your calendar." => "O ficheiro não continha nenhuns eventos ou então todos os eventos já estavam carregados no seu calendário", +"events has been saved in the new calendar" => "Os eventos foram guardados no novo calendário", +"Import failed" => "Falha na importação", +"events has been saved in your calendar" => "Os eventos foram guardados no seu calendário", "New Timezone:" => "Nova zona horária", "Timezone changed" => "Zona horária alterada", "Invalid request" => "Pedido inválido", "Calendar" => "Calendário", +"ddd" => "ddd", +"ddd M/d" => "ddd M/d", +"dddd M/d" => "dddd M/d", +"MMMM yyyy" => "MMMM aaaa", "MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "MMM d[ yyyy]{ '—'[ MMM] d yyyy}", +"dddd, MMM d, yyyy" => "dddd, MMM d, aaaa", "Birthday" => "Dia de anos", "Business" => "Negócio", "Call" => "Telefonar", @@ -22,7 +33,9 @@ "Projects" => "Projetos", "Questions" => "Perguntas", "Work" => "Trabalho", +"by" => "por", "unnamed" => "não definido", +"New Calendar" => "Novo calendário", "Does not repeat" => "Não repete", "Daily" => "Diário", "Weekly" => "Semanal", @@ -67,8 +80,26 @@ "by day and month" => "por dia e mês", "Date" => "Data", "Cal." => "Cal.", +"Sun." => "Dom.", +"Mon." => "Seg.", +"Tue." => "ter.", +"Wed." => "Qua.", +"Thu." => "Qui.", +"Fri." => "Sex.", +"Sat." => "Sáb.", +"Jan." => "Jan.", +"Feb." => "Fev,", +"Mar." => "Mar.", +"Apr." => "Abr.", +"May." => "Mai.", +"Jun." => "Jun.", +"Jul." => "Jul.", +"Aug." => "Ago.", +"Sep." => "Set.", +"Oct." => "Out.", +"Nov." => "Nov.", +"Dec." => "Dez.", "All day" => "Todo o dia", -"New Calendar" => "Novo calendário", "Missing fields" => "Falta campos", "Title" => "Título", "From Date" => "Da data", @@ -81,9 +112,7 @@ "Month" => "Mês", "List" => "Lista", "Today" => "Hoje", -"Calendars" => "Calendários", -"There was a fail, while parsing the file." => "Houve uma falha durante a análise do ficheiro", -"Choose active calendars" => "Escolhe calendários ativos", +"Settings" => "Configurações", "Your calendars" => "Os seus calendários", "CalDav Link" => "Endereço CalDav", "Shared calendars" => "Calendários partilhados", @@ -132,10 +161,12 @@ "Interval" => "Intervalo", "End" => "Fim", "occurrences" => "ocorrências", -"Import a calendar file" => "Importar um ficheiro de calendário", -"Please choose the calendar" => "Por favor escolhe o calendário", "create a new calendar" => "criar novo calendário", +"Import a calendar file" => "Importar um ficheiro de calendário", +"Please choose a calendar" => "Escolha um calendário por favor", "Name of new calendar" => "Nome do novo calendário", +"Take an available name!" => "Escolha um nome disponível!", +"A Calendar with this name already exists. If you continue anyhow, these calendars will be merged." => "Já existe um Calendário com esse nome. Se mesmo assim continuar, esses calendários serão fundidos.", "Import" => "Importar", "Importing calendar" => "A importar calendário", "Calendar imported successfully" => "Calendário importado com sucesso", @@ -143,16 +174,23 @@ "Create a new event" => "Criar novo evento", "View an event" => "Ver um evento", "No categories selected" => "Nenhuma categoria seleccionada", -"Select category" => "Selecionar categoria", "of" => "de", "at" => "em", +"General" => "Geral", "Timezone" => "Zona horária", -"Check always for changes of the timezone" => "Verificar sempre por alterações na zona horária", -"Timeformat" => "Formato da hora", +"Update timezone automatically" => "Actualizar automaticamente o fuso horário", +"Time format" => "Formato da hora", "24h" => "24h", "12h" => "12h", -"First day of the week" => "Primeiro dia da semana", -"Calendar CalDAV syncing address:" => "Endereço de sincronização CalDav do calendário", +"Start week on" => "Começar semana em", +"Cache" => "Memória de pré-carregamento", +"Clear cache for repeating events" => "Limpar a memória de pré carregamento para eventos recorrentes", +"URLs" => "Endereço(s) web", +"Calendar CalDAV syncing addresses" => "Endereços de sincronização de calendários CalDAV", +"more info" => "mais informação", +"Primary address (Kontact et al)" => "Endereço principal (contactos et al.)", +"iOS/OS X" => "iOS/OS X", +"Read only iCalendar link(s)" => "Ligaç(ão/ões) só de leitura do iCalendar", "Users" => "Utilizadores", "select users" => "Selecione utilizadores", "Editable" => "Editavel", diff --git a/apps/calendar/l10n/ro.php b/apps/calendar/l10n/ro.php index 550afcd102..696322dd73 100644 --- a/apps/calendar/l10n/ro.php +++ b/apps/calendar/l10n/ro.php @@ -23,6 +23,7 @@ "Questions" => "Întrebări", "Work" => "Servici", "unnamed" => "fără nume", +"New Calendar" => "Calendar nou", "Does not repeat" => "Nerepetabil", "Daily" => "Zilnic", "Weekly" => "Săptămânal", @@ -68,7 +69,6 @@ "Date" => "Data", "Cal." => "Cal.", "All day" => "Toată ziua", -"New Calendar" => "Calendar nou", "Missing fields" => "Câmpuri lipsă", "Title" => "Titlu", "From Date" => "Începând cu", @@ -81,9 +81,6 @@ "Month" => "Luna", "List" => "Listă", "Today" => "Astăzi", -"Calendars" => "Calendare", -"There was a fail, while parsing the file." => "A fost întâmpinată o eroare în procesarea fișierului", -"Choose active calendars" => "Alege calendarele active", "Your calendars" => "Calendarele tale", "CalDav Link" => "Legătură CalDav", "Shared calendars" => "Calendare partajate", @@ -132,6 +129,7 @@ "Interval" => "Interval", "End" => "Sfârșit", "occurrences" => "repetiții", +"create a new calendar" => "crează un calendar nou", "Import a calendar file" => "Importă un calendar", "Please choose the calendar" => "Alegeți calendarul", "create a new calendar" => "crează un calendar nou", @@ -143,7 +141,6 @@ "Create a new event" => "Crează un eveniment nou", "View an event" => "Vizualizează un eveniment", "No categories selected" => "Nici o categorie selectată", -"Select category" => "Selecteză categoria", "of" => "din", "at" => "la", "Timezone" => "Fus orar", @@ -151,8 +148,6 @@ "Timeformat" => "Forma de afișare a orei", "24h" => "24h", "12h" => "12h", -"First day of the week" => "Prima zi a săptămînii", -"Calendar CalDAV syncing address:" => "Adresa pentru sincronizarea calendarului CalDAV", "Users" => "Utilizatori", "select users" => "utilizatori selectați", "Editable" => "Editabil", diff --git a/apps/calendar/l10n/ru.php b/apps/calendar/l10n/ru.php index af40b06b9f..fbf5ec5ff7 100644 --- a/apps/calendar/l10n/ru.php +++ b/apps/calendar/l10n/ru.php @@ -1,11 +1,23 @@ "Не все календари полностью кешированы", +"Everything seems to be completely cached" => "Все, вроде бы, закешировано", "No calendars found." => "Календари не найдены.", "No events found." => "События не найдены.", "Wrong calendar" => "Неверный календарь", +"The file contained either no events or all events are already saved in your calendar." => "Файл либо не собержит событий, либо все события уже есть в календаре", +"events has been saved in the new calendar" => "события были сохранены в новый календарь", +"Import failed" => "Ошибка импорта", +"events has been saved in your calendar" => "события были сохранены в вашем календаре", "New Timezone:" => "Новый часовой пояс:", "Timezone changed" => "Часовой пояс изменён", "Invalid request" => "Неверный запрос", "Calendar" => "Календарь", +"ddd" => "ддд", +"ddd M/d" => "ддд М/д", +"dddd M/d" => "дддд М/д", +"MMMM yyyy" => "ММММ гггг", +"MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "MMM d[ yyyy]{ '—'[ MMM] d yyyy}", +"dddd, MMM d, yyyy" => "дддд, МММ д, гггг", "Birthday" => "День рождения", "Business" => "Бизнес", "Call" => "Звонить", @@ -21,7 +33,9 @@ "Projects" => "Проекты", "Questions" => "Вопросы", "Work" => "Работа", +"by" => "до свидания", "unnamed" => "без имени", +"New Calendar" => "Новый Календарь", "Does not repeat" => "Не повторяется", "Daily" => "Ежедневно", "Weekly" => "Еженедельно", @@ -66,8 +80,26 @@ "by day and month" => "по дню и месяцу", "Date" => "Дата", "Cal." => "Кал.", +"Sun." => "Вс.", +"Mon." => "Пн.", +"Tue." => "Вт.", +"Wed." => "Ср.", +"Thu." => "Чт.", +"Fri." => "Пт.", +"Sat." => "Сб.", +"Jan." => "Янв.", +"Feb." => "Фев.", +"Mar." => "Мар.", +"Apr." => "Апр.", +"May." => "Май.", +"Jun." => "Июн.", +"Jul." => "Июл.", +"Aug." => "Авг.", +"Sep." => "Сен.", +"Oct." => "Окт.", +"Nov." => "Ноя.", +"Dec." => "Дек.", "All day" => "Весь день", -"New Calendar" => "Новый Календарь", "Missing fields" => "Незаполненные поля", "Title" => "Название", "From Date" => "Дата начала", @@ -80,9 +112,7 @@ "Month" => "Месяц", "List" => "Список", "Today" => "Сегодня", -"Calendars" => "Календари", -"There was a fail, while parsing the file." => "Не удалось обработать файл.", -"Choose active calendars" => "Выберите активные календари", +"Settings" => "Параметры", "Your calendars" => "Ваши календари", "CalDav Link" => "Ссылка для CalDav", "Shared calendars" => "Общие календари", @@ -131,10 +161,12 @@ "Interval" => "Интервал", "End" => "Окончание", "occurrences" => "повторений", -"Import a calendar file" => "Импортировать календарь из файла", -"Please choose the calendar" => "Пожалуйста, выберите календарь", "create a new calendar" => "Создать новый календарь", +"Import a calendar file" => "Импортировать календарь из файла", +"Please choose a calendar" => "Пожалуйста, выберите календарь", "Name of new calendar" => "Название нового календаря", +"Take an available name!" => "Возьмите разрешенное имя!", +"A Calendar with this name already exists. If you continue anyhow, these calendars will be merged." => "Календарь с таким именем уже существует. Если вы продолжите, одноименный календарь будет удален.", "Import" => "Импортировать", "Importing calendar" => "Импортируется календарь", "Calendar imported successfully" => "Календарь успешно импортирован", @@ -142,14 +174,23 @@ "Create a new event" => "Создать новое событие", "View an event" => "Показать событие", "No categories selected" => "Категории не выбраны", -"Select category" => "Выбрать категорию", +"of" => "из", +"at" => "на", +"General" => "Основные", "Timezone" => "Часовой пояс", -"Check always for changes of the timezone" => "Всегда проверяйте изменение часового пояса", -"Timeformat" => "Формат времени", +"Update timezone automatically" => "Автоматическое обновление временной зоны", +"Time format" => "Формат времени", "24h" => "24ч", "12h" => "12ч", -"First day of the week" => "Первый день недели", -"Calendar CalDAV syncing address:" => "Адрес синхронизации календаря CalDAV:", +"Start week on" => "Начало недели", +"Cache" => "Кэш", +"Clear cache for repeating events" => "Очистить кэш повторяющихся событий", +"URLs" => "URLs", +"Calendar CalDAV syncing addresses" => "Адрес синхронизации CalDAV", +"more info" => "подробнее", +"Primary address (Kontact et al)" => "Основной адрес (Контакта)", +"iOS/OS X" => "iOS/OS X", +"Read only iCalendar link(s)" => "Читать только ссылки iCalendar", "Users" => "Пользователи", "select users" => "выбрать пользователей", "Editable" => "Редактируемо", diff --git a/apps/calendar/l10n/sk_SK.php b/apps/calendar/l10n/sk_SK.php index e182a9d3ea..65400c496d 100644 --- a/apps/calendar/l10n/sk_SK.php +++ b/apps/calendar/l10n/sk_SK.php @@ -6,7 +6,12 @@ "Timezone changed" => "Časové pásmo zmenené", "Invalid request" => "Neplatná požiadavka", "Calendar" => "Kalendár", +"ddd" => "ddd", +"ddd M/d" => "ddd M/d", +"dddd M/d" => "dddd M/d", +"MMMM yyyy" => "MMMM rrrr", "MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "d. MMM[ yyyy]{ '—' d.[ MMM] yyyy}", +"dddd, MMM d, yyyy" => "dddd, MMM d, rrrr", "Birthday" => "Narodeniny", "Business" => "Podnikanie", "Call" => "Hovor", @@ -23,6 +28,7 @@ "Questions" => "Otázky", "Work" => "Práca", "unnamed" => "nepomenovaný", +"New Calendar" => "Nový kalendár", "Does not repeat" => "Neopakovať", "Daily" => "Denne", "Weekly" => "Týždenne", @@ -68,7 +74,6 @@ "Date" => "Dátum", "Cal." => "Kal.", "All day" => "Celý deň", -"New Calendar" => "Nový kalendár", "Missing fields" => "Nevyplnené položky", "Title" => "Nadpis", "From Date" => "Od dátumu", @@ -81,9 +86,6 @@ "Month" => "Mesiac", "List" => "Zoznam", "Today" => "Dnes", -"Calendars" => "Kalendáre", -"There was a fail, while parsing the file." => "Nastala chyba počas parsovania súboru.", -"Choose active calendars" => "Zvoľte aktívne kalendáre", "Your calendars" => "Vaše kalendáre", "CalDav Link" => "CalDav odkaz", "Shared calendars" => "Zdielané kalendáre", @@ -132,27 +134,19 @@ "Interval" => "Interval", "End" => "Koniec", "occurrences" => "výskyty", -"Import a calendar file" => "Importovať súbor kalendára", -"Please choose the calendar" => "Prosím zvoľte kalendár", "create a new calendar" => "vytvoriť nový kalendár", +"Import a calendar file" => "Importovať súbor kalendára", "Name of new calendar" => "Meno nového kalendára", "Import" => "Importovať", -"Importing calendar" => "Importujem kalendár", -"Calendar imported successfully" => "Kalendár úspešne importovaný", "Close Dialog" => "Zatvoriť dialóg", "Create a new event" => "Vytvoriť udalosť", "View an event" => "Zobraziť udalosť", "No categories selected" => "Žiadne vybraté kategórie", -"Select category" => "Vybrať kategóriu", "of" => "z", "at" => "v", "Timezone" => "Časová zóna", -"Check always for changes of the timezone" => "Vždy kontroluj zmeny časového pásma", -"Timeformat" => "Formát času", "24h" => "24h", "12h" => "12h", -"First day of the week" => "Prvý deň v týždni", -"Calendar CalDAV syncing address:" => "Synchronizačná adresa kalendára CalDAV: ", "Users" => "Používatelia", "select users" => "vybrať používateľov", "Editable" => "Upravovateľné", diff --git a/apps/calendar/l10n/sl.php b/apps/calendar/l10n/sl.php index 3bf03ede12..585132314b 100644 --- a/apps/calendar/l10n/sl.php +++ b/apps/calendar/l10n/sl.php @@ -1,12 +1,23 @@ "Vsi koledarji niso povsem predpomnjeni", +"Everything seems to be completely cached" => "Izgleda, da je vse v predpomnilniku", "No calendars found." => "Ni bilo najdenih koledarjev.", "No events found." => "Ni bilo najdenih dogodkov.", "Wrong calendar" => "Napačen koledar", +"The file contained either no events or all events are already saved in your calendar." => "Datoteka ni vsebovala dogodkov ali pa so vsi dogodki že shranjeni v koledarju.", +"events has been saved in the new calendar" => "dogodki so bili shranjeni v nov koledar", +"Import failed" => "Uvoz je spodletel", +"events has been saved in your calendar" => "dogodki so bili shranjeni v vaš koledar", "New Timezone:" => "Nov časovni pas:", "Timezone changed" => "Časovni pas je bil spremenjen", "Invalid request" => "Neveljaven zahtevek", "Calendar" => "Koledar", +"ddd" => "ddd", +"ddd M/d" => "ddd M/d", +"dddd M/d" => "dddd M/d", +"MMMM yyyy" => "MMMM yyyy", "MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "MMM d[ yyyy]{ '—'[ MMM] d yyyy}", +"dddd, MMM d, yyyy" => "dddd, MMM d, yyyy", "Birthday" => "Rojstni dan", "Business" => "Poslovno", "Call" => "Pokliči", @@ -22,7 +33,9 @@ "Projects" => "Projekt", "Questions" => "Vprašanja", "Work" => "Delo", +"by" => "od", "unnamed" => "neimenovan", +"New Calendar" => "Nov koledar", "Does not repeat" => "Se ne ponavlja", "Daily" => "Dnevno", "Weekly" => "Tedensko", @@ -67,8 +80,26 @@ "by day and month" => "po dnevu in mesecu", "Date" => "Datum", "Cal." => "Kol.", +"Sun." => "ned.", +"Mon." => "pon.", +"Tue." => "tor.", +"Wed." => "sre.", +"Thu." => "čet.", +"Fri." => "pet.", +"Sat." => "sob.", +"Jan." => "jan.", +"Feb." => "feb.", +"Mar." => "mar.", +"Apr." => "apr.", +"May." => "maj", +"Jun." => "jun.", +"Jul." => "jul.", +"Aug." => "avg.", +"Sep." => "sep.", +"Oct." => "okt.", +"Nov." => "nov.", +"Dec." => "dec.", "All day" => "Cel dan", -"New Calendar" => "Nov koledar", "Missing fields" => "Mankajoča polja", "Title" => "Naslov", "From Date" => "od Datum", @@ -81,9 +112,7 @@ "Month" => "Mesec", "List" => "Seznam", "Today" => "Danes", -"Calendars" => "Koledarji", -"There was a fail, while parsing the file." => "Pri razčlenjevanju datoteke je prišlo do napake.", -"Choose active calendars" => "Izberite aktivne koledarje", +"Settings" => "Nastavitve", "Your calendars" => "Vaši koledarji", "CalDav Link" => "CalDav povezava", "Shared calendars" => "Koledarji v souporabi", @@ -132,27 +161,34 @@ "Interval" => "Časovni razmik", "End" => "Konec", "occurrences" => "ponovitev", -"Import a calendar file" => "Uvozi datoteko koledarja", -"Please choose the calendar" => "Izberi koledar", "create a new calendar" => "Ustvari nov koledar", +"Import a calendar file" => "Uvozi datoteko koledarja", +"Please choose a calendar" => "Prosimo, če izberete koledar", "Name of new calendar" => "Ime novega koledarja", +"Take an available name!" => "Izberite prosto ime!", +"A Calendar with this name already exists. If you continue anyhow, these calendars will be merged." => "Koledar s tem imenom že obstaja. Če nadaljujete, bosta koledarja združena.", "Import" => "Uvozi", -"Importing calendar" => "Uvažam koledar", -"Calendar imported successfully" => "Koledar je bil uspešno uvožen", "Close Dialog" => "Zapri dialog", "Create a new event" => "Ustvari nov dogodek", "View an event" => "Poglej dogodek", "No categories selected" => "Nobena kategorija ni izbrana", -"Select category" => "Izberi kategorijo", "of" => "od", "at" => "pri", +"General" => "Splošno", "Timezone" => "Časovni pas", -"Check always for changes of the timezone" => "Vedno preveri za spremembe časovnega pasu", -"Timeformat" => "Zapis časa", +"Update timezone automatically" => "Samodejno posodobi časovni pas", +"Time format" => "Oblika zapisa časa", "24h" => "24ur", "12h" => "12ur", -"First day of the week" => "Prvi dan v tednu", -"Calendar CalDAV syncing address:" => "CalDAV sinhronizacijski naslov koledarja:", +"Start week on" => "Začni teden z", +"Cache" => "Predpomnilnik", +"Clear cache for repeating events" => "Počisti predpomnilnik za ponavljajoče dogodke", +"URLs" => "URLji", +"Calendar CalDAV syncing addresses" => "CalDAV naslov za usklajevanje koledarjev", +"more info" => "dodatne informacije", +"Primary address (Kontact et al)" => "Glavni naslov (Kontakt et al)", +"iOS/OS X" => "iOS/OS X", +"Read only iCalendar link(s)" => "iCalendar povezava/e samo za branje", "Users" => "Uporabniki", "select users" => "izberite uporabnike", "Editable" => "Možno urejanje", diff --git a/apps/calendar/l10n/sr.php b/apps/calendar/l10n/sr.php index 5798c66e0a..4ec60e20cb 100644 --- a/apps/calendar/l10n/sr.php +++ b/apps/calendar/l10n/sr.php @@ -18,6 +18,7 @@ "Projects" => "Пројекти", "Questions" => "Питања", "Work" => "Посао", +"New Calendar" => "Нови календар", "Does not repeat" => "Не понавља се", "Daily" => "дневно", "Weekly" => "недељно", @@ -26,15 +27,11 @@ "Monthly" => "месечно", "Yearly" => "годишње", "All day" => "Цео дан", -"New Calendar" => "Нови календар", "Title" => "Наслов", "Week" => "Недеља", "Month" => "Месец", "List" => "Списак", "Today" => "Данас", -"Calendars" => "Календари", -"There was a fail, while parsing the file." => "дошло је до грешке при расчлањивању фајла.", -"Choose active calendars" => "Изаберите активне календаре", "CalDav Link" => "КалДав веза", "Download" => "Преузми", "Edit" => "Уреди", @@ -59,6 +56,5 @@ "Description of the Event" => "Опис догађаја", "Repeat" => "Понављај", "Create a new event" => "Направи нови догађај", -"Select category" => "Изаберите категорију", "Timezone" => "Временска зона" ); diff --git a/apps/calendar/l10n/sr@latin.php b/apps/calendar/l10n/sr@latin.php index c261f903f7..4ceabcbae5 100644 --- a/apps/calendar/l10n/sr@latin.php +++ b/apps/calendar/l10n/sr@latin.php @@ -18,6 +18,7 @@ "Projects" => "Projekti", "Questions" => "Pitanja", "Work" => "Posao", +"New Calendar" => "Novi kalendar", "Does not repeat" => "Ne ponavlja se", "Daily" => "dnevno", "Weekly" => "nedeljno", @@ -26,15 +27,11 @@ "Monthly" => "mesečno", "Yearly" => "godišnje", "All day" => "Ceo dan", -"New Calendar" => "Novi kalendar", "Title" => "Naslov", "Week" => "Nedelja", "Month" => "Mesec", "List" => "Spisak", "Today" => "Danas", -"Calendars" => "Kalendari", -"There was a fail, while parsing the file." => "došlo je do greške pri rasčlanjivanju fajla.", -"Choose active calendars" => "Izaberite aktivne kalendare", "CalDav Link" => "KalDav veza", "Download" => "Preuzmi", "Edit" => "Uredi", @@ -59,6 +56,5 @@ "Description of the Event" => "Opis događaja", "Repeat" => "Ponavljaj", "Create a new event" => "Napravi novi događaj", -"Select category" => "Izaberite kategoriju", "Timezone" => "Vremenska zona" ); diff --git a/apps/calendar/l10n/sv.php b/apps/calendar/l10n/sv.php index 59f8c6e6b5..4cea9073a2 100644 --- a/apps/calendar/l10n/sv.php +++ b/apps/calendar/l10n/sv.php @@ -1,12 +1,23 @@ "Alla kalendrar är inte fullständigt sparade i cache", +"Everything seems to be completely cached" => "Allt verkar vara fullständigt sparat i cache", "No calendars found." => "Inga kalendrar funna", "No events found." => "Inga händelser funna.", "Wrong calendar" => "Fel kalender", +"The file contained either no events or all events are already saved in your calendar." => "Filen innehöll inga händelser eller så är alla händelser redan sparade i kalendern.", +"events has been saved in the new calendar" => "händelser har sparats i den nya kalendern", +"Import failed" => "Misslyckad import", +"events has been saved in your calendar" => "händelse har sparats i din kalender", "New Timezone:" => "Ny tidszon:", "Timezone changed" => "Tidszon ändrad", "Invalid request" => "Ogiltig begäran", "Calendar" => "Kalender", +"ddd" => "ddd", +"ddd M/d" => "ddd M/d", +"dddd M/d" => "dddd M/d", +"MMMM yyyy" => "MMMM åååå", "MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "MMM d[ yyyy]{ '—'[ MMM] d yyyy}", +"dddd, MMM d, yyyy" => "ddd, MMM d, åååå", "Birthday" => "Födelsedag", "Business" => "Företag", "Call" => "Ringa", @@ -22,7 +33,9 @@ "Projects" => "Projekt", "Questions" => "Frågor", "Work" => "Arbetet", +"by" => "av", "unnamed" => "Namn saknas", +"New Calendar" => "Ny kalender", "Does not repeat" => "Upprepas inte", "Daily" => "Dagligen", "Weekly" => "Varje vecka", @@ -67,8 +80,26 @@ "by day and month" => "efter dag och månad", "Date" => "Datum", "Cal." => "Kal.", +"Sun." => "Sön.", +"Mon." => "Mån.", +"Tue." => "Tis.", +"Wed." => "Ons.", +"Thu." => "Tor.", +"Fri." => "Fre.", +"Sat." => "Lör.", +"Jan." => "Jan.", +"Feb." => "Feb.", +"Mar." => "Mar.", +"Apr." => "Apr.", +"May." => "Maj.", +"Jun." => "Jun.", +"Jul." => "Jul.", +"Aug." => "Aug.", +"Sep." => "Sep.", +"Oct." => "Okt.", +"Nov." => "Nov.", +"Dec." => "Dec.", "All day" => "Hela dagen", -"New Calendar" => "Ny kalender", "Missing fields" => "Saknade fält", "Title" => "Rubrik", "From Date" => "Från datum", @@ -81,9 +112,6 @@ "Month" => "Månad", "List" => "Lista", "Today" => "Idag", -"Calendars" => "Kalendrar", -"There was a fail, while parsing the file." => "Det blev ett fel medan filen analyserades.", -"Choose active calendars" => "Välj aktiva kalendrar", "Your calendars" => "Dina kalendrar", "CalDav Link" => "CalDAV-länk", "Shared calendars" => "Delade kalendrar", @@ -132,10 +160,12 @@ "Interval" => "Hur ofta", "End" => "Slut", "occurrences" => "Händelser", -"Import a calendar file" => "Importera en kalenderfil", -"Please choose the calendar" => "Välj kalender", "create a new calendar" => "skapa en ny kalender", +"Import a calendar file" => "Importera en kalenderfil", +"Please choose a calendar" => "Välj en kalender", "Name of new calendar" => "Namn på ny kalender", +"Take an available name!" => "Ta ett ledigt namn!", +"A Calendar with this name already exists. If you continue anyhow, these calendars will be merged." => "En kalender med detta namn finns redan. Om du fortsätter ändå så kommer dessa kalendrar att slås samman.", "Import" => "Importera", "Importing calendar" => "Importerar kalender", "Calendar imported successfully" => "Kalender importerades utan problem", @@ -143,7 +173,6 @@ "Create a new event" => "Skapa en ny händelse", "View an event" => "Visa en händelse", "No categories selected" => "Inga kategorier valda", -"Select category" => "Välj kategori", "of" => "av", "at" => "på", "Timezone" => "Tidszon", @@ -151,8 +180,13 @@ "Timeformat" => "Tidsformat", "24h" => "24h", "12h" => "12h", -"First day of the week" => "Första dagen av veckan", -"Calendar CalDAV syncing address:" => "Synkroniseringsadress för CalDAV kalender:", +"Cache" => "Cache", +"Clear cache for repeating events" => "Töm cache för upprepade händelser", +"Calendar CalDAV syncing addresses" => "Kalender CalDAV synkroniserar adresser", +"more info" => "mer info", +"Primary address (Kontact et al)" => "Primary address (Kontact et al)", +"iOS/OS X" => "iOS/OS X", +"Read only iCalendar link(s)" => "Read only iCalendar link(s)", "Users" => "Användare", "select users" => "välj användare", "Editable" => "Redigerbar", diff --git a/apps/calendar/l10n/th_TH.php b/apps/calendar/l10n/th_TH.php index 8aaa7ae756..0b92a623d4 100644 --- a/apps/calendar/l10n/th_TH.php +++ b/apps/calendar/l10n/th_TH.php @@ -1,12 +1,23 @@ "ไม่ใช่ปฏิทินทั้งหมดที่จะถูกจัดเก็บข้อมูลไว้ในหน่วยความจำแคชอย่างสมบูรณ์", +"Everything seems to be completely cached" => "ทุกสิ่งทุกอย่างได้ถูกเก็บเข้าไปไว้ในหน่วยความจำแคชอย่างสมบูรณ์แล้ว", "No calendars found." => "ไม่พบปฏิทินที่ต้องการ", "No events found." => "ไม่พบกิจกรรมที่ต้องการ", "Wrong calendar" => "ปฏิทินไม่ถูกต้อง", +"The file contained either no events or all events are already saved in your calendar." => "ไฟล์ดังกล่าวบรรจุข้อมูลกิจกรรมที่มีอยู่แล้วในปฏิทินของคุณ", +"events has been saved in the new calendar" => "กิจกรรมได้ถูกบันทึกไปไว้ในปฏิทินที่สร้างขึ้นใหม่แล้ว", +"Import failed" => "การนำเข้าข้อมูลล้มเหลว", +"events has been saved in your calendar" => "กิจกรรมได้ถูกบันทึกเข้าไปไว้ในปฏิทินของคุณแล้ว", "New Timezone:" => "สร้างโซนเวลาใหม่:", "Timezone changed" => "โซนเวลาถูกเปลี่ยนแล้ว", "Invalid request" => "คำร้องขอไม่ถูกต้อง", "Calendar" => "ปฏิทิน", +"ddd" => "ddd", +"ddd M/d" => "ddd M/d", +"dddd M/d" => "dddd M/d", +"MMMM yyyy" => "MMMM yyyy", "MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "MMM d[ yyyy]{ '—'[ MMM] d yyyy}", +"dddd, MMM d, yyyy" => "dddd, MMM d, yyyy", "Birthday" => "วันเกิด", "Business" => "ธุรกิจ", "Call" => "โทรติดต่อ", @@ -22,7 +33,9 @@ "Projects" => "โครงการ", "Questions" => "คำถาม", "Work" => "งาน", +"by" => "โดย", "unnamed" => "ไม่มีชื่อ", +"New Calendar" => "สร้างปฏิทินใหม่", "Does not repeat" => "ไม่ต้องทำซ้ำ", "Daily" => "รายวัน", "Weekly" => "รายสัปดาห์", @@ -67,8 +80,26 @@ "by day and month" => "ตามวันและเดือน", "Date" => "วันที่", "Cal." => "คำนวณ", +"Sun." => "อา.", +"Mon." => "จ.", +"Tue." => "อ.", +"Wed." => "พ.", +"Thu." => "พฤ.", +"Fri." => "ศ.", +"Sat." => "ส.", +"Jan." => "ม.ค.", +"Feb." => "ก.พ.", +"Mar." => "มี.ค.", +"Apr." => "เม.ย.", +"May." => "พ.ค.", +"Jun." => "มิ.ย.", +"Jul." => "ก.ค.", +"Aug." => "ส.ค.", +"Sep." => "ก.ย.", +"Oct." => "ต.ค.", +"Nov." => "พ.ย.", +"Dec." => "ธ.ค.", "All day" => "ทั้งวัน", -"New Calendar" => "สร้างปฏิทินใหม่", "Missing fields" => "ช่องฟิลด์เกิดการสูญหาย", "Title" => "ชื่อกิจกรรม", "From Date" => "จากวันที่", @@ -81,9 +112,7 @@ "Month" => "เดือน", "List" => "รายการ", "Today" => "วันนี้", -"Calendars" => "ปฏิทิน", -"There was a fail, while parsing the file." => "เกิดความล้มเหลวในการแยกไฟล์", -"Choose active calendars" => "เลือกปฏิทินที่ต้องการใช้งาน", +"Settings" => "ตั้งค่า", "Your calendars" => "ปฏิทินของคุณ", "CalDav Link" => "ลิงค์ CalDav", "Shared calendars" => "ปฏิทินที่เปิดแชร์", @@ -132,27 +161,34 @@ "Interval" => "ช่วงเวลา", "End" => "สิ้นสุด", "occurrences" => "จำนวนที่ปรากฏ", -"Import a calendar file" => "นำเข้าไฟล์ปฏิทิน", -"Please choose the calendar" => "กรณาเลือกปฏิทิน", "create a new calendar" => "สร้างปฏิทินใหม่", +"Import a calendar file" => "นำเข้าไฟล์ปฏิทิน", +"Please choose a calendar" => "กรุณาเลือกปฏิทิน", "Name of new calendar" => "ชื่อของปฏิทิน", +"Take an available name!" => "เลือกชื่อที่ต้องการ", +"A Calendar with this name already exists. If you continue anyhow, these calendars will be merged." => "ปฏิทินชื่อดังกล่าวถูกใช้งานไปแล้ว หากคุณยังดำเนินการต่อไป ปฏิทินดังกล่าวนี้จะถูกผสานข้อมูลเข้าด้วยกัน", "Import" => "นำเข้าข้อมูล", -"Importing calendar" => "นำเข้าข้อมูลปฏิทิน", -"Calendar imported successfully" => "ปฏิทินถูกนำเข้าข้อมูลเรียบร้อยแล้ว", "Close Dialog" => "ปิดกล่องข้อความโต้ตอบ", "Create a new event" => "สร้างกิจกรรมใหม่", "View an event" => "ดูกิจกรรม", "No categories selected" => "ยังไม่ได้เลือกหมวดหมู่", -"Select category" => "เลือกหมวดหมู่", "of" => "ของ", "at" => "ที่", +"General" => "ทั่วไป", "Timezone" => "โซนเวลา", -"Check always for changes of the timezone" => "ตรวจสอบการเปลี่ยนแปลงโซนเวลาอยู่เสมอ", -"Timeformat" => "รูปแบบการแสดงเวลา", +"Update timezone automatically" => "อัพเดทโซนเวลาอัตโนมัติ", +"Time format" => "รูปแบบเวลา", "24h" => "24 ช.ม.", "12h" => "12 ช.ม.", -"First day of the week" => "วันแรกของสัปดาห์", -"Calendar CalDAV syncing address:" => "ที่อยู่ในการเชื่อมข้อมูลกับปฏิทิน CalDav:", +"Start week on" => "เริ่มต้นสัปดาห์ด้วย", +"Cache" => "หน่วยความจำแคช", +"Clear cache for repeating events" => "ล้างข้อมูลในหน่วยความจำแคชสำหรับกิจกรรมที่ซ้ำซ้อน", +"URLs" => "URLs", +"Calendar CalDAV syncing addresses" => "ที่อยู่ที่ใช้สำหรับเชื่อมข้อมูลปฏิทิน CalDAV", +"more info" => "ข้อมูลเพิ่มเติม", +"Primary address (Kontact et al)" => "ที่อยู่หลัก (Kontact et al)", +"iOS/OS X" => "iOS/OS X", +"Read only iCalendar link(s)" => "อ่านเฉพาะลิงก์ iCalendar เท่านั้น", "Users" => "ผู้ใช้งาน", "select users" => "เลือกผู้ใช้งาน", "Editable" => "สามารถแก้ไขได้", diff --git a/apps/calendar/l10n/tr.php b/apps/calendar/l10n/tr.php index a72e4c39f6..b9256eb619 100644 --- a/apps/calendar/l10n/tr.php +++ b/apps/calendar/l10n/tr.php @@ -1,12 +1,23 @@ "Bütün takvimler tamamen ön belleğe alınmadı", +"Everything seems to be completely cached" => "Bütün herşey tamamen ön belleğe alınmış görünüyor", "No calendars found." => "Takvim yok.", "No events found." => "Etkinlik yok.", "Wrong calendar" => "Yanlış takvim", +"The file contained either no events or all events are already saved in your calendar." => "Dosya ya hiçbir etkinlik içermiyor veya bütün etkinlikler takviminizde zaten saklı.", +"events has been saved in the new calendar" => "Etkinlikler yeni takvimde saklandı", +"Import failed" => "İçeri aktarma başarısız oldu.", +"events has been saved in your calendar" => "Etkinlikler takviminizde saklandı", "New Timezone:" => "Yeni Zamandilimi:", "Timezone changed" => "Zaman dilimi değiştirildi", "Invalid request" => "Geçersiz istek", "Calendar" => "Takvim", +"ddd" => "ddd", +"ddd M/d" => "ddd M/d", +"dddd M/d" => "dddd M/d", +"MMMM yyyy" => "MMMM yyyy", "MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "AAA g[ yyyy]{ '—'[ AAA] g yyyy}", +"dddd, MMM d, yyyy" => "dddd, MMM d, yyyy", "Birthday" => "Doğum günü", "Business" => "İş", "Call" => "Arama", @@ -22,7 +33,9 @@ "Projects" => "Projeler", "Questions" => "Sorular", "Work" => "İş", +"by" => "hazırlayan", "unnamed" => "isimsiz", +"New Calendar" => "Yeni Takvim", "Does not repeat" => "Tekrar etmiyor", "Daily" => "Günlük", "Weekly" => "Haftalı", @@ -67,8 +80,26 @@ "by day and month" => "gün ve aya göre", "Date" => "Tarih", "Cal." => "Takv.", +"Sun." => "Paz.", +"Mon." => "Pzt.", +"Tue." => "Sal.", +"Wed." => "Çar.", +"Thu." => "Per.", +"Fri." => "Cum.", +"Sat." => "Cmt.", +"Jan." => "Oca.", +"Feb." => "Şbt.", +"Mar." => "Mar.", +"Apr." => "Nis", +"May." => "May.", +"Jun." => "Haz.", +"Jul." => "Tem.", +"Aug." => "Agu.", +"Sep." => "Eyl.", +"Oct." => "Eki.", +"Nov." => "Kas.", +"Dec." => "Ara.", "All day" => "Tüm gün", -"New Calendar" => "Yeni Takvim", "Missing fields" => "Eksik alanlar", "Title" => "Başlık", "From Date" => "Bu Tarihten", @@ -81,9 +112,6 @@ "Month" => "Ay", "List" => "Liste", "Today" => "Bugün", -"Calendars" => "Takvimler", -"There was a fail, while parsing the file." => "Dosya okunurken başarısızlık oldu.", -"Choose active calendars" => "Aktif takvimleri seçin", "Your calendars" => "Takvimleriniz", "CalDav Link" => "CalDav Bağlantısı", "Shared calendars" => "Paylaşılan", @@ -132,27 +160,29 @@ "Interval" => "Aralık", "End" => "Son", "occurrences" => "olaylar", -"Import a calendar file" => "Takvim dosyasını içeri aktar", -"Please choose the calendar" => "Lütfen takvimi seçin", "create a new calendar" => "Yeni bir takvim oluştur", +"Import a calendar file" => "Takvim dosyasını içeri aktar", +"Please choose a calendar" => "Lütfen takvim seçiniz", "Name of new calendar" => "Yeni takvimin adı", +"Take an available name!" => "Müsait ismi al !", +"A Calendar with this name already exists. If you continue anyhow, these calendars will be merged." => "Bu isimde bir takvim zaten mevcut. Yine de devam ederseniz bu takvimler birleştirilecektir.", "Import" => "İçe Al", -"Importing calendar" => "Takvim içe aktarılıyor", -"Calendar imported successfully" => "Takvim başarıyla içe aktarıldı", "Close Dialog" => "Diyalogu kapat", "Create a new event" => "Yeni olay oluştur", "View an event" => "Bir olay görüntüle", "No categories selected" => "Kategori seçilmedi", -"Select category" => "Kategori seçin", "of" => "nın", "at" => "üzerinde", "Timezone" => "Zaman dilimi", -"Check always for changes of the timezone" => "Sürekli zaman dilimi değişikliklerini kontrol et", -"Timeformat" => "Saat biçimi", "24h" => "24s", "12h" => "12s", -"First day of the week" => "Haftanın ilk günü", -"Calendar CalDAV syncing address:" => "CalDAV Takvim eşzamanlama adresi:", +"Cache" => "Önbellek", +"Clear cache for repeating events" => "Tekrar eden etkinlikler için ön belleği temizle.", +"Calendar CalDAV syncing addresses" => "CalDAV takvimi adresleri senkronize ediyor.", +"more info" => "daha fazla bilgi", +"Primary address (Kontact et al)" => "Öncelikli adres", +"iOS/OS X" => "iOS/OS X", +"Read only iCalendar link(s)" => "Sadece okunabilir iCalendar link(ler)i", "Users" => "Kullanıcılar", "select users" => "kullanıcıları seç", "Editable" => "Düzenlenebilir", diff --git a/apps/calendar/l10n/uk.php b/apps/calendar/l10n/uk.php index 892896742d..2911307e58 100644 --- a/apps/calendar/l10n/uk.php +++ b/apps/calendar/l10n/uk.php @@ -16,6 +16,7 @@ "Projects" => "Проекти", "Questions" => "Запитання", "Work" => "Робота", +"New Calendar" => "новий Календар", "Does not repeat" => "Не повторювати", "Daily" => "Щоденно", "Weekly" => "Щотижня", @@ -52,21 +53,29 @@ "Date" => "Дата", "Cal." => "Кал.", "All day" => "Увесь день", -"New Calendar" => "новий Календар", +"Missing fields" => "Пропущені поля", "Title" => "Назва", +"From Date" => "Від Дати", +"From Time" => "З Часу", +"To Date" => "До Часу", +"To Time" => "По Дату", +"The event ends before it starts" => "Подія завершається до її початку", +"There was a database fail" => "Сталася помилка бази даних", "Week" => "Тиждень", "Month" => "Місяць", "List" => "Список", "Today" => "Сьогодні", -"Calendars" => "Календарі", -"There was a fail, while parsing the file." => "Сталася помилка при обробці файлу", -"Choose active calendars" => "Вибрати активні календарі", +"Your calendars" => "Ваші календарі", "Download" => "Завантажити", "Edit" => "Редагувати", +"Delete" => "Видалити", "New calendar" => "Новий календар", "Edit calendar" => "Редагувати календар", "Active" => "Активний", "Calendar color" => "Колір календаря", +"Save" => "Зберегти", +"Cancel" => "Відмінити", +"Export" => "Експорт", "Title of the Event" => "Назва події", "Category" => "Категорія", "From" => "З", @@ -76,14 +85,14 @@ "Description" => "Опис", "Description of the Event" => "Опис події", "Repeat" => "Повторювати", -"Import a calendar file" => "Імпортувати файл календаря", "create a new calendar" => "створити новий календар", +"Import a calendar file" => "Імпортувати файл календаря", "Name of new calendar" => "Назва нового календаря", -"Calendar imported successfully" => "Календар успішно імпортовано", +"Import" => "Імпорт", "Create a new event" => "Створити нову подію", "Timezone" => "Часовий пояс", -"Timeformat" => "Формат часу", "24h" => "24г", "12h" => "12г", -"Calendar CalDAV syncing address:" => "Адреса синхронізації календаря CalDAV:" +"Users" => "Користувачі", +"Groups" => "Групи" ); diff --git a/apps/calendar/l10n/vi.php b/apps/calendar/l10n/vi.php new file mode 100644 index 0000000000..3594a095eb --- /dev/null +++ b/apps/calendar/l10n/vi.php @@ -0,0 +1,131 @@ + "Không tìm thấy lịch.", +"No events found." => "Không tìm thấy sự kiện nào", +"Wrong calendar" => "Sai lịch", +"New Timezone:" => "Múi giờ mới :", +"Timezone changed" => "Thay đổi múi giờ", +"Invalid request" => "Yêu cầu không hợp lệ", +"Calendar" => "Lịch", +"ddd" => "ddd", +"ddd M/d" => "ddd M/d", +"dddd M/d" => "dddd M/d", +"MMMM yyyy" => "MMMM yyyy", +"MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "MMM d[ yyyy]{ '—'[ MMM] d yyyy}", +"dddd, MMM d, yyyy" => "dddd, MMM d, yyyy", +"Birthday" => "Ngày sinh nhật", +"Business" => "Công việc", +"Call" => "Số điện thoại", +"Clients" => "Máy trạm", +"Holidays" => "Ngày lễ", +"Ideas" => "Ý tưởng", +"Jubilee" => "Lễ kỷ niệm", +"Meeting" => "Hội nghị", +"Other" => "Khác", +"Personal" => "Cá nhân", +"Projects" => "Dự án", +"Questions" => "Câu hỏi", +"Work" => "Công việc", +"New Calendar" => "Lịch mới", +"Does not repeat" => "Không lặp lại", +"Daily" => "Hàng ngày", +"Weekly" => "Hàng tuần", +"Every Weekday" => "Mỗi ngày trong tuần", +"Bi-Weekly" => "Hai tuần một lần", +"Monthly" => "Hàng tháng", +"Yearly" => "Hàng năm", +"never" => "không thay đổi", +"by occurrences" => "bởi xuất hiện", +"by date" => "bởi ngày", +"by monthday" => "bởi ngày trong tháng", +"by weekday" => "bởi ngày trong tuần", +"Monday" => "Thứ 2", +"Tuesday" => "Thứ 3", +"Wednesday" => "Thứ 4", +"Thursday" => "Thứ 5", +"Friday" => "Thứ ", +"Saturday" => "Thứ 7", +"Sunday" => "Chủ nhật", +"events week of month" => "sự kiện trong tuần của tháng", +"first" => "đầu tiên", +"second" => "Thứ hai", +"third" => "Thứ ba", +"fourth" => "Thứ tư", +"fifth" => "Thứ năm", +"January" => "Tháng 1", +"February" => "Tháng 2", +"March" => "Tháng 3", +"April" => "Tháng 4", +"May" => "Tháng 5", +"June" => "Tháng 6", +"July" => "Tháng 7", +"August" => "Tháng 8", +"September" => "Tháng 9", +"October" => "Tháng 10", +"November" => "Tháng 11", +"December" => "Tháng 12", +"by events date" => "Theo ngày tháng sự kiện", +"by weeknumber(s)" => "số tuần", +"by day and month" => "ngày, tháng", +"Date" => "Ngày", +"Cal." => "Cal.", +"All day" => "Tất cả các ngày", +"Title" => "Tiêu đề", +"From Date" => "Từ ngày", +"From Time" => "Từ thời gian", +"To Date" => "Tới ngày", +"To Time" => "Tới thời gian", +"The event ends before it starts" => "Sự kiện này kết thúc trước khi nó bắt đầu", +"Week" => "Tuần", +"Month" => "Tháng", +"List" => "Danh sách", +"Today" => "Hôm nay", +"Your calendars" => "Lịch của bạn", +"CalDav Link" => "Liên kết CalDav ", +"Shared calendars" => "Chia sẻ lịch", +"No shared calendars" => "Không chia sẻ lcihj", +"Share Calendar" => "Chia sẻ lịch", +"Download" => "Tải về", +"Edit" => "Chỉnh sửa", +"Delete" => "Xóa", +"shared with you by" => "Chia sẻ bởi", +"New calendar" => "Lịch mới", +"Edit calendar" => "sửa Lịch", +"Displayname" => "Hiển thị tên", +"Active" => "Kích hoạt", +"Calendar color" => "Màu lịch", +"Save" => "Lưu", +"Submit" => "Submit", +"Cancel" => "Hủy", +"Edit an event" => "Sửa sự kiện", +"Share" => "Chia sẻ", +"Title of the Event" => "Tên sự kiện", +"Category" => "Danh mục", +"All Day Event" => "Sự kiện trong ngày", +"From" => "Từ", +"To" => "Tới", +"Advanced options" => "Tùy chọn nâng cao", +"Location" => "Nơi", +"Location of the Event" => "Nơi tổ chức sự kiện", +"Description" => "Mô tả", +"Description of the Event" => "Mô tả sự kiện", +"Repeat" => "Lặp lại", +"Advanced" => "Nâng cao", +"Select weekdays" => "Chọn ngày trong tuần", +"Select days" => "Chọn ngày", +"and the events day of year." => "và sự kiện của ngày trong năm", +"and the events day of month." => "và sự kiện của một ngày trong năm", +"Select months" => "Chọn tháng", +"Select weeks" => "Chọn tuần", +"and the events week of year." => "và sự kiện của tuần trong năm.", +"create a new calendar" => "Tạo lịch mới", +"Name of new calendar" => "Tên lịch mới", +"Close Dialog" => "Đóng hộp thoại", +"Create a new event" => "Tạo một sự kiện mới", +"View an event" => "Xem một sự kiện", +"No categories selected" => "Không danh sách nào được chọn", +"of" => "của", +"at" => "tại", +"Timezone" => "Múi giờ", +"24h" => "24h", +"12h" => "12h" +); diff --git a/apps/calendar/l10n/zh_CN.GB2312.php b/apps/calendar/l10n/zh_CN.GB2312.php new file mode 100644 index 0000000000..38f039e661 --- /dev/null +++ b/apps/calendar/l10n/zh_CN.GB2312.php @@ -0,0 +1,121 @@ + "错误的日历", +"New Timezone:" => "新时区", +"Timezone changed" => "时区改变了", +"Invalid request" => "非法请求", +"Calendar" => "日历", +"Birthday" => "生日", +"Business" => "商务", +"Call" => "呼叫", +"Clients" => "客户端", +"Deliverer" => "交付者", +"Holidays" => "假期", +"Ideas" => "灵感", +"Journey" => "旅行", +"Jubilee" => "五十年纪念", +"Meeting" => "会面", +"Other" => "其它", +"Personal" => "个人的", +"Projects" => "项目", +"Questions" => "问题", +"Work" => "工作", +"New Calendar" => "新的日历", +"Does not repeat" => "不要重复", +"Daily" => "每天", +"Weekly" => "每星期", +"Every Weekday" => "每个周末", +"Bi-Weekly" => "每两周", +"Monthly" => "每个月", +"Yearly" => "每年", +"never" => "从不", +"by occurrences" => "根据发生时", +"by date" => "根据日期", +"by monthday" => "根据月天", +"by weekday" => "根据星期", +"Monday" => "星期一", +"Tuesday" => "星期二", +"Wednesday" => "星期三", +"Thursday" => "星期四", +"Friday" => "星期五", +"Saturday" => "星期六", +"Sunday" => "星期天", +"events week of month" => "时间每月发生的周数", +"first" => "首先", +"second" => "其次", +"third" => "第三", +"fourth" => "第四", +"fifth" => "第五", +"last" => "最后", +"January" => "一月", +"February" => "二月", +"March" => "三月", +"April" => "四月", +"May" => "五月", +"June" => "六月", +"July" => "七月", +"August" => "八月", +"September" => "九月", +"October" => "十月", +"November" => "十一月", +"December" => "十二月", +"by events date" => "根据时间日期", +"by yearday(s)" => "根据年数", +"by weeknumber(s)" => "根据周数", +"by day and month" => "根据天和月", +"Date" => "日期", +"Cal." => "Cal.", +"All day" => "整天", +"Missing fields" => "丢失的输入框", +"Title" => "标题", +"From Date" => "从日期", +"From Time" => "从时间", +"To Date" => "到日期", +"To Time" => "到时间", +"The event ends before it starts" => "在它开始前需要结束的事件", +"There was a database fail" => "发生了一个数据库失败", +"Week" => "星期", +"Month" => "月", +"List" => "列表", +"Today" => "今天", +"CalDav Link" => "CalDav 链接", +"Download" => "下载", +"Edit" => "编辑", +"Delete" => "删除", +"New calendar" => "新的日历", +"Edit calendar" => "编辑日历", +"Displayname" => "显示名称", +"Active" => "活动", +"Calendar color" => "日历颜色", +"Save" => "保存", +"Submit" => "提交", +"Cancel" => " 取消", +"Edit an event" => "编辑一个事件", +"Export" => "导出", +"Title of the Event" => "事件的标题", +"Category" => "分类", +"All Day Event" => "每天的事件", +"From" => "从", +"To" => "到", +"Advanced options" => "进阶选项", +"Location" => "地点", +"Location of the Event" => "事件的地点", +"Description" => "解释", +"Description of the Event" => "事件描述", +"Repeat" => "重复", +"Advanced" => "进阶", +"Select weekdays" => "选择星期", +"Select days" => "选择日", +"and the events day of year." => "选择每年时间发生天数", +"and the events day of month." => "选择每个月事件发生的天", +"Select months" => "选择月份", +"Select weeks" => "选择星期", +"and the events week of year." => "每年时间发生的星期", +"Interval" => "间隔", +"End" => "结束", +"occurrences" => "发生", +"Import" => "导入", +"Create a new event" => "新建一个时间", +"Timezone" => "时区", +"24h" => "24小时", +"12h" => "12小时" +); diff --git a/apps/calendar/l10n/zh_CN.php b/apps/calendar/l10n/zh_CN.php index bb7e0a2872..add84588d3 100644 --- a/apps/calendar/l10n/zh_CN.php +++ b/apps/calendar/l10n/zh_CN.php @@ -6,6 +6,7 @@ "Timezone changed" => "时区已修改", "Invalid request" => "非法请求", "Calendar" => "日历", +"ddd" => "ddd", "MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "MMM d[ yyyy]{ '—'[ MMM] d yyyy}", "Birthday" => "生日", "Business" => "商务", @@ -23,6 +24,7 @@ "Questions" => "问题", "Work" => "工作", "unnamed" => "未命名", +"New Calendar" => "新日历", "Does not repeat" => "不重复", "Daily" => "每天", "Weekly" => "每周", @@ -68,7 +70,6 @@ "Date" => "日期", "Cal." => "日历", "All day" => "全天", -"New Calendar" => "新日历", "Missing fields" => "缺少字段", "Title" => "标题", "From Date" => "从", @@ -132,6 +133,7 @@ "Interval" => "间隔", "End" => "结束", "occurrences" => "次", +"create a new calendar" => "创建新日历", "Import a calendar file" => "导入日历文件", "Please choose the calendar" => "请选择日历", "create a new calendar" => "创建新日历", @@ -143,7 +145,6 @@ "Create a new event" => "创建新事件", "View an event" => "查看事件", "No categories selected" => "无选中分类", -"Select category" => "选择分类", "of" => "在", "at" => "在", "Timezone" => "时区", diff --git a/apps/calendar/l10n/zh_TW.php b/apps/calendar/l10n/zh_TW.php index 746594462c..48897b8ca0 100644 --- a/apps/calendar/l10n/zh_TW.php +++ b/apps/calendar/l10n/zh_TW.php @@ -23,6 +23,7 @@ "Questions" => "問題", "Work" => "工作", "unnamed" => "無名稱的", +"New Calendar" => "新日曆", "Does not repeat" => "不重覆", "Daily" => "每日", "Weekly" => "每週", @@ -68,7 +69,6 @@ "Date" => "日期", "Cal." => "行事曆", "All day" => "整天", -"New Calendar" => "新日曆", "Missing fields" => "遺失欄位", "Title" => "標題", "From Date" => "自日期", @@ -81,9 +81,6 @@ "Month" => "月", "List" => "清單", "Today" => "今日", -"Calendars" => "日曆", -"There was a fail, while parsing the file." => "解析檔案時失敗。", -"Choose active calendars" => "選擇一個作用中的日曆", "Your calendars" => "你的行事曆", "CalDav Link" => "CalDav 聯結", "Shared calendars" => "分享的行事曆", @@ -132,6 +129,7 @@ "Interval" => "間隔", "End" => "結束", "occurrences" => "事件", +"create a new calendar" => "建立新日曆", "Import a calendar file" => "匯入日曆檔案", "Please choose the calendar" => "請選擇日曆", "create a new calendar" => "建立新日曆", @@ -143,7 +141,6 @@ "Create a new event" => "建立一個新事件", "View an event" => "觀看一個活動", "No categories selected" => "沒有選擇分類", -"Select category" => "選擇分類", "of" => "於", "at" => "於", "Timezone" => "時區", @@ -151,7 +148,6 @@ "Timeformat" => "日期格式", "24h" => "24小時制", "12h" => "12小時制", -"First day of the week" => "每週的第一天", "Calendar CalDAV syncing address:" => "CalDAV 的日曆同步地址:", "Users" => "使用者", "select users" => "選擇使用者", diff --git a/apps/calendar/lib/app.php b/apps/calendar/lib/app.php index 6c55bd1988..8bdb54f486 100644 --- a/apps/calendar/lib/app.php +++ b/apps/calendar/lib/app.php @@ -9,26 +9,26 @@ * This class manages our app actions */ OC_Calendar_App::$l10n = new OC_L10N('calendar'); -OC_Calendar_App::$tz = OCP\Config::getUserValue(OCP\USER::getUser(), 'calendar', 'timezone', date_default_timezone_get()); +OC_Calendar_App::$tz = OC_Calendar_App::getTimezone(); class OC_Calendar_App{ const CALENDAR = 'calendar'; const EVENT = 'event'; - /* + /** * @brief language object for calendar app */ public static $l10n; - /* + /** * @brief categories of the user */ protected static $categories = null; - /* + /** * @brief timezone of the user */ public static $tz; - /* + /** * @brief returns informations about a calendar * @param int $id - id of the calendar * @param bool $security - check access rights or not @@ -50,13 +50,10 @@ class OC_Calendar_App{ return false; } } - if($calendar === false){ - return false; - } - return OC_Calendar_Calendar::find($id); + return $calendar; } - /* + /** * @brief returns informations about an event * @param int $id - id of the event * @param bool $security - check access rights or not @@ -82,7 +79,7 @@ class OC_Calendar_App{ return $event; } - /* + /** * @brief returns the parsed calendar data * @param int $id - id of the event * @param bool $security - check access rights or not @@ -100,7 +97,7 @@ class OC_Calendar_App{ return $vobject; } - /* + /** * @brief checks if an event was edited and dies if it was * @param (object) $vevent - vevent object of the event * @param (int) $lastmodified - time of last modification as unix timestamp @@ -115,12 +112,11 @@ class OC_Calendar_App{ return true; } - /* + /** * @brief returns the default categories of ownCloud * @return (array) $categories */ - protected static function getDefaultCategories() - { + protected static function getDefaultCategories(){ return array( self::$l10n->t('Birthday'), self::$l10n->t('Business'), @@ -140,7 +136,7 @@ class OC_Calendar_App{ ); } - /* + /** * @brief returns the vcategories object of the user * @return (object) $vcategories */ @@ -151,12 +147,11 @@ class OC_Calendar_App{ return self::$categories; } - /* + /** * @brief returns the categories of the vcategories object * @return (array) $categories */ - public static function getCategoryOptions() - { + public static function getCategoryOptions(){ $categories = self::getVCategories()->categories(); return $categories; } @@ -199,17 +194,24 @@ class OC_Calendar_App{ } else if (isset($calendar->VTODO)) { $object = $calendar->VTODO; + } else + if (isset($calendar->VJOURNAL)) { + $object = $calendar->VJOURNAL; } if ($object) { self::getVCategories()->loadFromVObject($object, true); } } - + + /** + * @brief returns the options for the repeat rule of an repeating event + * @return array - valid inputs for the repeat rule of an repeating event + */ public static function getRepeatOptions(){ return OC_Calendar_Object::getRepeatOptions(self::$l10n); } - /* + /** * @brief returns the options for the end of an repeating event * @return array - valid inputs for the end of an repeating events */ @@ -217,7 +219,7 @@ class OC_Calendar_App{ return OC_Calendar_Object::getEndOptions(self::$l10n); } - /* + /** * @brief returns the options for an monthly repeating event * @return array - valid inputs for monthly repeating events */ @@ -225,7 +227,7 @@ class OC_Calendar_App{ return OC_Calendar_Object::getMonthOptions(self::$l10n); } - /* + /** * @brief returns the options for an weekly repeating event * @return array - valid inputs for weekly repeating events */ @@ -233,7 +235,7 @@ class OC_Calendar_App{ return OC_Calendar_Object::getWeeklyOptions(self::$l10n); } - /* + /** * @brief returns the options for an yearly repeating event * @return array - valid inputs for yearly repeating events */ @@ -241,7 +243,7 @@ class OC_Calendar_App{ return OC_Calendar_Object::getYearOptions(self::$l10n); } - /* + /** * @brief returns the options for an yearly repeating event which occurs on specific days of the year * @return array - valid inputs for yearly repeating events */ @@ -249,7 +251,7 @@ class OC_Calendar_App{ return OC_Calendar_Object::getByYearDayOptions(); } - /* + /** * @brief returns the options for an yearly repeating event which occurs on specific month of the year * @return array - valid inputs for yearly repeating events */ @@ -257,7 +259,7 @@ class OC_Calendar_App{ return OC_Calendar_Object::getByMonthOptions(self::$l10n); } - /* + /** * @brief returns the options for an yearly repeating event which occurs on specific week numbers of the year * @return array - valid inputs for yearly repeating events */ @@ -265,7 +267,7 @@ class OC_Calendar_App{ return OC_Calendar_Object::getByWeekNoOptions(); } - /* + /** * @brief returns the options for an yearly or monthly repeating event which occurs on specific days of the month * @return array - valid inputs for yearly or monthly repeating events */ @@ -273,15 +275,25 @@ class OC_Calendar_App{ return OC_Calendar_Object::getByMonthDayOptions(); } - /* + /** * @brief returns the options for an monthly repeating event which occurs on specific weeks of the month * @return array - valid inputs for monthly repeating events */ public static function getWeekofMonth(){ return OC_Calendar_Object::getWeekofMonth(self::$l10n); } - - /* + + /** + * @return (string) $timezone as set by user or the default timezone + */ + public static function getTimezone() { + return OCP\Config::getUserValue(OCP\User::getUser(), + 'calendar', + 'timezone', + date_default_timezone_get()); + } + + /** * @brief checks the access for a calendar / an event * @param (int) $id - id of the calendar / event * @param (string) $type - type of the id (calendar/event) @@ -322,7 +334,7 @@ class OC_Calendar_App{ } } - /* + /** * @brief analyses the parameter for calendar parameter and returns the objects * @param (string) $calendarid - calendarid * @param (int) $start - unixtimestamp of start @@ -362,97 +374,62 @@ class OC_Calendar_App{ return $events; } - /* + /** * @brief generates the output for an event which will be readable for our js * @param (mixed) $event - event object / array - * @param (int) $start - unixtimestamp of start - * @param (int) $end - unixtimestamp of end + * @param (int) $start - DateTime object of start + * @param (int) $end - DateTime object of end * @return (array) $output - readable output */ public static function generateEventOutput($event, $start, $end){ - $output = array(); - - if(isset($event['calendardata'])){ - $object = OC_VObject::parse($event['calendardata']); - $vevent = $object->VEVENT; - }else{ - $vevent = $event['vevent']; + if(!isset($event['calendardata']) && !isset($event['vevent'])){ + return false; } - + if(!isset($event['calendardata']) && isset($event['vevent'])){ + $event['calendardata'] = "BEGIN:VCALENDAR\nVERSION:2.0\nPRODID:ownCloud's Internal iCal System\n" . $event['vevent']->serialize() . "END:VCALENDAR"; + } + $object = OC_VObject::parse($event['calendardata']); + $vevent = $object->VEVENT; + $return = array(); + $id = $event['id']; + $allday = ($vevent->DTSTART->getDateType() == Sabre_VObject_Element_DateTime::DATE)?true:false; $last_modified = @$vevent->__get('LAST-MODIFIED'); $lastmodified = ($last_modified)?$last_modified->getDateTime()->format('U'):0; - - $output = array('id'=>(int)$event['id'], + $staticoutput = array('id'=>(int)$event['id'], 'title' => ($event['summary']!=NULL || $event['summary'] != '')?$event['summary']: self::$l10n->t('unnamed'), 'description' => isset($vevent->DESCRIPTION)?$vevent->DESCRIPTION->value:'', - 'lastmodified'=>$lastmodified); - - $dtstart = $vevent->DTSTART; - $start_dt = $dtstart->getDateTime(); - $dtend = OC_Calendar_Object::getDTEndFromVEvent($vevent); - $end_dt = $dtend->getDateTime(); - - if ($dtstart->getDateType() == Sabre_VObject_Element_DateTime::DATE){ - $output['allDay'] = true; - }else{ - $output['allDay'] = false; - $start_dt->setTimezone(new DateTimeZone(self::$tz)); - $end_dt->setTimezone(new DateTimeZone(self::$tz)); - } - - // Handle exceptions to recurring events - $exceptionDateObjects = $vevent->select('EXDATE'); - $exceptionDateMap = Array(); - foreach ($exceptionDateObjects as $exceptionObject) { - foreach($exceptionObject->getDateTimes() as $datetime) { - $ts = $datetime->getTimestamp(); - $exceptionDateMap[idate('Y',$ts)][idate('m', $ts)][idate('d', $ts)] = true; - } - } - - $return = array(); - if($event['repeating'] == 1){ - $duration = (double) $end_dt->format('U') - (double) $start_dt->format('U'); - $r = new When(); - $r->recur($start_dt)->rrule((string) $vevent->RRULE); - /*$r = new iCal_Repeat_Generator(array('RECUR' => $start_dt, - * 'RRULE' => (string)$vevent->RRULE - * 'RDATE' => (string)$vevent->RDATE - * 'EXRULE' => (string)$vevent->EXRULE - * 'EXDATE' => (string)$vevent->EXDATE));*/ - while($result = $r->next()){ - if($result < $start){ - continue; - } - if($result > $end){ - break; - } - // Check for exceptions to recurring events - $ts = $result->getTimestamp(); - if (isset($exceptionDateMap[idate('Y',$ts)][idate('m', $ts)][idate('d', $ts)])) { - continue; - } - unset($ts); - - if($output['allDay'] == true){ - $output['start'] = $result->format('Y-m-d'); - $output['end'] = date('Y-m-d', $result->format('U') + --$duration); + 'lastmodified'=>$lastmodified, + 'allDay'=>$allday); + if(OC_Calendar_Object::isrepeating($id) && OC_Calendar_Repeat::is_cached_inperiod($event['id'], $start, $end)){ + $cachedinperiod = OC_Calendar_Repeat::get_inperiod($id, $start, $end); + foreach($cachedinperiod as $cachedevent){ + $dynamicoutput = array(); + if($allday){ + $start_dt = new DateTime($cachedevent['startdate'], new DateTimeZone('UTC')); + $end_dt = new DateTime($cachedevent['enddate'], new DateTimeZone('UTC')); + $dynamicoutput['start'] = $start_dt->format('Y-m-d'); + $dynamicoutput['end'] = $end_dt->format('Y-m-d'); }else{ - $output['start'] = $result->format('Y-m-d H:i:s'); - $output['end'] = date('Y-m-d H:i:s', $result->format('U') + $result->format('Z') + $duration); + $start_dt = new DateTime($cachedevent['startdate'], new DateTimeZone('UTC')); + $end_dt = new DateTime($cachedevent['enddate'], new DateTimeZone('UTC')); + $start_dt->setTimezone(new DateTimeZone(self::$tz)); + $end_dt->setTimezone(new DateTimeZone(self::$tz)); + $dynamicoutput['start'] = $start_dt->format('Y-m-d H:i:s'); + $dynamicoutput['end'] = $end_dt->format('Y-m-d H:i:s'); } - $return[] = $output; + $return[] = array_merge($staticoutput, $dynamicoutput); } }else{ - if($output['allDay'] == true){ - $output['start'] = $start_dt->format('Y-m-d'); - $end_dt->modify('-1 sec'); - $output['end'] = $end_dt->format('Y-m-d'); - }else{ - $output['start'] = $start_dt->format('Y-m-d H:i:s'); - $output['end'] = $end_dt->format('Y-m-d H:i:s'); + if(OC_Calendar_Object::isrepeating($id) || $event['repeating'] == 1){ + $object->expand($start, $end); + } + foreach($object->getComponents() as $singleevent){ + if(!($singleevent instanceof Sabre_VObject_Component_VEvent)){ + continue; + } + $dynamicoutput = OC_Calendar_Object::generateStartEndDate($singleevent->DTSTART, OC_Calendar_Object::getDTEndFromVEvent($singleevent), $allday, self::$tz); + $return[] = array_merge($staticoutput, $dynamicoutput); } - $return[] = $output; } return $return; } diff --git a/apps/calendar/lib/calendar.php b/apps/calendar/lib/calendar.php index 09cbee204d..f8f5aab363 100644 --- a/apps/calendar/lib/calendar.php +++ b/apps/calendar/lib/calendar.php @@ -5,24 +5,11 @@ * later. * See the COPYING-README file. */ -/* +/** * * The following SQL statement is just a help for developers and will not be * executed! * - * CREATE TABLE calendar_objects ( - * id INTEGER UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, - * calendarid INTEGER UNSIGNED NOT NULL, - * objecttype VARCHAR(40) NOT NULL, - * startdate DATETIME, - * enddate DATETIME, - * repeating INT(1), - * summary VARCHAR(255), - * calendardata TEXT, - * uri VARCHAR(100), - * lastmodified INT(11) - * ); - * * CREATE TABLE calendar_calendars ( * id INTEGER UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, * userid VARCHAR(255), @@ -35,6 +22,7 @@ * timezone TEXT, * components VARCHAR(20) * ); + * */ /** @@ -44,10 +32,10 @@ class OC_Calendar_Calendar{ /** * @brief Returns the list of calendars for a specific user. * @param string $uid User ID - * @param boolean $active Only return calendars with this $active state, default(=null) is don't care + * @param boolean $active Only return calendars with this $active state, default(=false) is don't care * @return array */ - public static function allCalendars($uid, $active=null){ + public static function allCalendars($uid, $active=false){ $values = array($uid); $active_where = ''; if (!is_null($active) && $active){ @@ -109,7 +97,10 @@ class OC_Calendar_Calendar{ $stmt = OCP\DB::prepare( 'INSERT INTO `*PREFIX*calendar_calendars` (`userid`,`displayname`,`uri`,`ctag`,`calendarorder`,`calendarcolor`,`timezone`,`components`) VALUES(?,?,?,?,?,?,?,?)' ); $result = $stmt->execute(array($userid,$name,$uri,1,$order,$color,$timezone,$components)); - return OCP\DB::insertid('*PREFIX*calendar_calendars'); + $insertid = OCP\DB::insertid('*PREFIX*calendar_calendars'); + OCP\Util::emitHook('OC_Calendar', 'addCalendar', $insertid); + + return $insertid; } /** @@ -129,7 +120,10 @@ class OC_Calendar_Calendar{ $stmt = OCP\DB::prepare( 'INSERT INTO `*PREFIX*calendar_calendars` (`userid`,`displayname`,`uri`,`ctag`,`calendarorder`,`calendarcolor`,`timezone`,`components`) VALUES(?,?,?,?,?,?,?,?)' ); $result = $stmt->execute(array($userid,$name,$uri,1,$order,$color,$timezone,$components)); - return OCP\DB::insertid('*PREFIX*calendar_calendars'); + $insertid = OCP\DB::insertid('*PREFIX*calendar_calendars'); + OCP\Util::emitHook('OC_Calendar', 'addCalendar', $insertid); + + return $insertid; } /** @@ -158,6 +152,7 @@ class OC_Calendar_Calendar{ $stmt = OCP\DB::prepare( 'UPDATE `*PREFIX*calendar_calendars` SET `displayname`=?,`calendarorder`=?,`calendarcolor`=?,`timezone`=?,`components`=?,`ctag`=`ctag`+1 WHERE `id`=?' ); $result = $stmt->execute(array($name,$order,$color,$timezone,$components,$id)); + OCP\Util::emitHook('OC_Calendar', 'editCalendar', $id); return true; } @@ -198,9 +193,27 @@ class OC_Calendar_Calendar{ $stmt = OCP\DB::prepare( 'DELETE FROM `*PREFIX*calendar_objects` WHERE `calendarid` = ?' ); $stmt->execute(array($id)); + OCP\Util::emitHook('OC_Calendar', 'deleteCalendar', $id); + if(count(self::allCalendars(OCP\USER::getUser())) == 0) { + self::addCalendar(OCP\USER::getUser(),'Default calendar'); + } + return true; } - + + /** + * @brief merges two calendars + * @param integer $id1 + * @param integer $id2 + * @return boolean + */ + public static function mergeCalendar($id1, $id2){ + $stmt = OCP\DB::prepare('UPDATE `*PREFIX*calendar_objects` SET `calendarid` = ? WHERE `calendarid` = ?'); + $stmt->execute(array($id1, $id2)); + self::touchCalendar($id1); + self::deleteCalendar($id2); + } + /** * @brief Creates a URI for Calendar * @param string $name name of the calendar @@ -226,6 +239,11 @@ class OC_Calendar_Calendar{ list($prefix,$userid) = Sabre_DAV_URLUtil::splitPath($principaluri); return $userid; } + + /** + * @brief returns the possible color for calendars + * @return array + */ public static function getCalendarColorOptions(){ return array( '#ff0000', // "Red" @@ -239,13 +257,52 @@ class OC_Calendar_Calendar{ ); } + /** + * @brief generates the Event Source Info for our JS + * @param array $calendar calendar data + * @return array + */ public static function getEventSourceInfo($calendar){ return array( 'url' => OCP\Util::linkTo('calendar', 'ajax/events.php').'?calendar_id='.$calendar['id'], 'backgroundColor' => $calendar['calendarcolor'], 'borderColor' => '#888', - 'textColor' => 'black', + 'textColor' => self::generateTextColor($calendar['calendarcolor']), 'cache' => true, ); } + + /* + * @brief checks if a calendar name is available for a user + * @param string $calendarname + * @param string $userid + * @return boolean + */ + public static function isCalendarNameavailable($calendarname, $userid){ + $calendars = self::allCalendars($userid); + foreach($calendars as $calendar){ + if($calendar['displayname'] == $calendarname){ + return false; + } + } + return true; + } + + /* + * @brief generates the text color for the calendar + * @param string $calendarcolor rgb calendar color code in hex format (with or without the leading #) + * (this function doesn't pay attention on the alpha value of rgba color codes) + * @return boolean + */ + public static function generateTextColor($calendarcolor){ + if(substr_count($calendarcolor, '#') == 1){ + $calendarcolor = substr($calendarcolor,1); + } + $red = hexdec(substr($calendarcolor,0,2)); + $green = hexdec(substr($calendarcolor,2,2)); + $blue = hexdec(substr($calendarcolor,2,2)); + //recommendation by W3C + $computation = ((($red * 299) + ($green * 587) + ($blue * 114)) / 1000); + return ($computation > 130)?'#000000':'#FAFAFA'; + } } diff --git a/apps/calendar/lib/export.php b/apps/calendar/lib/export.php new file mode 100644 index 0000000000..8f26891f36 --- /dev/null +++ b/apps/calendar/lib/export.php @@ -0,0 +1,94 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ +/** + * This class does export and converts all times to UTC + */ +class OC_Calendar_Export{ + /** + * @brief Use one of these constants as second parameter if you call OC_Calendar_Export::export() + */ + const CALENDAR = 'calendar'; + const EVENT = 'event'; + + /** + * @brief export a calendar or an event + * @param integer $id id of calendar / event + * @param string $type use OC_Calendar_Export constants + * @return string + */ + public static function export($id, $type){ + if($type == self::EVENT){ + $return = self::event($id); + }else{ + $return = self::calendar($id); + } + return self::fixLineBreaks($return); + } + + /** + * @brief exports a calendar and convert all times to UTC + * @param integer $id id of the calendar + * @return string + */ + private static function calendar($id){ + $events = OC_Calendar_Object::all($id); + $calendar = OC_Calendar_Calendar::find($id); + $return = "BEGIN:VCALENDAR\nVERSION:2.0\nPRODID:ownCloud Calendar " . OCP\App::getAppVersion('calendar') . "\nX-WR-CALNAME:" . $calendar['displayname'] . "\n"; + foreach($events as $event){ + $return .= self::generateEvent($event); + } + $return .= "END:VCALENDAR"; + return $return; + } + + /** + * @brief exports an event and convert all times to UTC + * @param integer $id id of the event + * @return string + */ + private static function event($id){ + $event = OC_Calendar_Object::find($id); + $return = "BEGIN:VCALENDAR\nVERSION:2.0\nPRODID:ownCloud Calendar " . OCP\App::getAppVersion('calendar') . "\nX-WR-CALNAME:" . $event['summary'] . "\n"; + $return .= self::generateEvent($event); + $return .= "END:VCALENDAR"; + return $return; + } + + /** + * @brief generates the VEVENT with UTC dates + * @param array $event + * @return string + */ + private static function generateEvent($event){ + $object = OC_VObject::parse($event['calendardata']); + $dtstart = $object->VEVENT->DTSTART; + $start_dt = $dtstart->getDateTime(); + $dtend = OC_Calendar_Object::getDTEndFromVEvent($object->VEVENT); + $end_dt = $dtend->getDateTime(); + if($dtstart->getDateType() !== Sabre_VObject_Element_DateTime::DATE){ + $start_dt->setTimezone(new DateTimeZone('UTC')); + $end_dt->setTimezone(new DateTimeZone('UTC')); + $object->VEVENT->setDateTime('DTSTART', $start_dt, Sabre_VObject_Property_DateTime::UTC); + $object->VEVENT->setDateTime('DTEND', $end_dt, Sabre_VObject_Property_DateTime::UTC); + } + return $object->VEVENT->serialize(); + } + + /** + * @brief fixes new line breaks + * (fixes problems with Apple iCal) + * @param string $string to fix + * @return string + */ + private static function fixLineBreaks($string){ + $string = str_replace("\r\n", "\n", $string); + $string = str_replace("\r", "\n", $string); + $string = str_replace("\n", "\r\n", $string); + return $string; + } +} diff --git a/apps/calendar/lib/import.php b/apps/calendar/lib/import.php new file mode 100644 index 0000000000..368f8406e7 --- /dev/null +++ b/apps/calendar/lib/import.php @@ -0,0 +1,336 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ +/* + * This class does import and converts all times to the users current timezone + */ +class OC_Calendar_Import{ + /* + * @brief counts the absolute number of parsed elements + */ + private $abscount; + + /* + * @brief var saves if the percentage should be saved with OC_Cache + */ + private $cacheprogress; + + /* + * @brief Sabre_VObject_Component_VCalendar object - for documentation see http://code.google.com/p/sabredav/wiki/Sabre_VObject_Component_VCalendar + */ + private $calobject; + + /* + * @brief var counts the number of imported elements + */ + private $count; + + /* + * @brief var to check if errors happend while initialization + */ + private $error; + + /* + * @brief var saves the ical string that was submitted with the __construct function + */ + private $ical; + + /* + * @brief calendar id for import + */ + private $id; + + /* + * @brief var saves the percentage of the import's progress + */ + private $progress; + + /* + * @brief var saves the key for the percentage of the import's progress + */ + private $progresskey; + + /* + * @brief var saves the timezone the events shell converted to + */ + private $tz; + + /* + * @brief var saves the userid + */ + private $userid; + + /* + * public methods + */ + + /* + * @brief does general initialization for import object + * @param string $calendar content of ical file + * @param string $tz timezone of the user + * @return boolean + */ + public function __construct($ical){ + $this->error = null; + $this->ical = $ical; + $this->abscount = 0; + $this->count = 0; + try{ + $this->calobject = OC_VObject::parse($this->ical); + }catch(Exception $e){ + //MISSING: write some log + $this->error = true; + return false; + } + return true; + } + + /* + * @brief imports a calendar + * @return boolean + */ + public function import(){ + if(!$this->isValid()){ + return false; + } + $numofcomponents = count($this->calobject->getComponents()); + foreach($this->calobject->getComponents() as $object){ + if(!($object instanceof Sabre_VObject_Component_VEvent) && !($object instanceof Sabre_VObject_Component_VJournal) && !($object instanceof Sabre_VObject_Component_VTodo)){ + continue; + } + $dtend = OC_Calendar_Object::getDTEndFromVEvent($object); + $object->DTSTART->getDateTime()->setTimezone(new DateTimeZone($this->tz)); + $object->DTEND->setDateTime($dtend->getDateTime(), $object->DTSTART->getDateType()); + $object->DTEND->getDateTime()->setTimezone(new DateTimeZone($this->tz)); + $vcalendar = $this->createVCalendar($object->serialize()); + $insertid = OC_Calendar_Object::add($this->id, $vcalendar); + $this->abscount++; + if($this->isDuplicate($insertid)){ + OC_Calendar_Object::delete($insertid); + }else{ + $this->count++; + } + $this->updateProgress(intval(($this->abscount / $numofcomponents)*100)); + } + OC_Cache::remove($this->progresskey); + return true; + } + + /* + * @brief sets the timezone + * @return boolean + */ + public function setTimeZone($tz){ + $this->tz = $tz; + return true; + } + + /* + * @brief sets the progresskey + * @return boolean + */ + public function setProgresskey($progresskey){ + $this->progresskey = $progresskey; + return true; + } + + /* + * @brief checks if something went wrong while initialization + * @return boolean + */ + public function isValid(){ + if(is_null($this->error)){ + return true; + } + return false; + } + + /* + * @brief returns the percentage of progress + * @return integer + */ + public function getProgress(){ + return $this->progress; + } + + /* + * @brief enables the cache for the percentage of progress + * @return boolean + */ + public function enableProgressCache(){ + $this->cacheprogress = true; + return true; + } + + /* + * @brief disables the cache for the percentage of progress + * @return boolean + */ + public function disableProgressCache(){ + $this->cacheprogress = false; + return false; + } + + /* + * @brief generates a new calendar name + * @return string + */ + public function createCalendarName(){ + $calendars = OC_Calendar_Calendar::allCalendars($this->userid); + $calendarname = $guessedcalendarname = !is_null($this->guessCalendarName())?($this->guessCalendarName()):(OC_Calendar_App::$l10n->t('New Calendar')); + $i = 1; + while(!OC_Calendar_Calendar::isCalendarNameavailable($calendarname, $this->userid)){ + $calendarname = $guessedcalendarname . ' (' . $i . ')'; + $i++; + } + return $calendarname; + } + + /* + * @brief generates a new calendar color + * @return string + */ + public function createCalendarColor(){ + if(is_null($this->guessCalendarColor())){ + return '#9fc6e7'; + } + return $this->guessCalendarColor(); + } + + /* + * @brief sets the id for the calendar + * @param integer $id of the calendar + * @return boolean + */ + public function setCalendarID($id){ + $this->id = $id; + return true; + } + + /* + * @brief sets the userid to import the calendar + * @param string $id of the user + * @return boolean + */ + public function setUserID($userid){ + $this->userid = $userid; + return true; + } + + /* + * @brief returns the private + * @param string $id of the user + * @return boolean + */ + public function getCount(){ + return $this->count; + } + + /* + * private methods + */ + + /* + * @brief generates an unique ID + * @return string + */ + //private function createUID(){ + // return substr(md5(rand().time()),0,10); + //} + + /* + * @brief checks is the UID is already in use for another event + * @param string $uid uid to check + * @return boolean + */ + //private function isUIDAvailable($uid){ + // + //} + + /* + * @brief generates a proper VCalendar string + * @param string $vobject + * @return string + */ + private function createVCalendar($vobject){ + if(is_object($vobject)){ + $vobject = @$vobject->serialize(); + } + $vcalendar = "BEGIN:VCALENDAR\nVERSION:2.0\nPRODID:ownCloud Calendar " . OCP\App::getAppVersion('calendar') . "\n"; + $vcalendar .= $vobject; + $vcalendar .= "END:VCALENDAR"; + return $vcalendar; + } + + /* + * @brief checks if an event already exists in the user's calendars + * @param integer $insertid id of the new object + * @return boolean + */ + private function isDuplicate($insertid){ + $newobject = OC_Calendar_Object::find($insertid); + $stmt = OCP\DB::prepare('SELECT COUNT(*) as count FROM *PREFIX*calendar_objects + INNER JOIN *PREFIX*calendar_calendars ON calendarid=*PREFIX*calendar_calendars.id + WHERE objecttype=? AND startdate=? AND enddate=? AND repeating=? AND summary=? AND calendardata=? AND userid = ?'); + $result = $stmt->execute(array($newobject['objecttype'],$newobject['startdate'],$newobject['enddate'],$newobject['repeating'],$newobject['summary'],$newobject['calendardata'], $this->userid)); + $result = $result->fetchRow(); + if($result['count'] >= 2){ + return true; + } + return false; + } + + /* + * @brief updates the progress var + * @param integer $percentage + * @return boolean + */ + private function updateProgress($percentage){ + $this->progress = $percentage; + if($this->cacheprogress){ + OC_Cache::set($this->progresskey, $this->progress, 300); + } + return true; + } + + /* + * public methods for (pre)rendering of X-... Attributes + */ + + /* + * @brief guesses the calendar color + * @return mixed - string or boolean + */ + public function guessCalendarColor(){ + if(!is_null($this->calobject->__get('X-APPLE-CALENDAR-COLOR'))){ + return $this->calobject->__get('X-APPLE-CALENDAR-COLOR'); + } + return null; + } + + /* + * @brief guesses the calendar description + * @return mixed - string or boolean + */ + public function guessCalendarDescription(){ + if(!is_null($this->calobject->__get('X-WR-CALDESC'))){ + return $this->calobject->__get('X-WR-CALDESC'); + } + return null; + } + + /* + * @brief guesses the calendar name + * @return mixed - string or boolean + */ + public function guessCalendarName(){ + if(!is_null($this->calobject->__get('X-WR-CALNAME'))){ + return $this->calobject->__get('X-WR-CALNAME'); + } + return null; + } +} diff --git a/apps/calendar/lib/object.php b/apps/calendar/lib/object.php index df866bd3c5..8020d7c2e5 100644 --- a/apps/calendar/lib/object.php +++ b/apps/calendar/lib/object.php @@ -1,10 +1,31 @@ + * Copyright (c) 2012 Bart Visscher + * Copyright (c) 2012 Georg Ehrke * This file is licensed under the Affero General Public License version 3 or * later. * See the COPYING-README file. */ + /** + * + * The following SQL statement is just a help for developers and will not be + * executed! + * + * CREATE TABLE calendar_objects ( + * id INTEGER UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, + * calendarid INTEGER UNSIGNED NOT NULL, + * objecttype VARCHAR(40) NOT NULL, + * startdate DATETIME, + * enddate DATETIME, + * repeating INT(1), + * summary VARCHAR(255), + * calendardata TEXT, + * uri VARCHAR(100), + * lastmodified INT(11) + * ); + * + */ /** * This class manages our calendar objects @@ -108,7 +129,7 @@ class OC_Calendar_Object{ $object_id = OCP\DB::insertid('*PREFIX*calendar_objects'); OC_Calendar_Calendar::touchCalendar($id); - + OCP\Util::emitHook('OC_Calendar', 'addEvent', $object_id); return $object_id; } @@ -128,7 +149,7 @@ class OC_Calendar_Object{ $object_id = OCP\DB::insertid('*PREFIX*calendar_objects'); OC_Calendar_Calendar::touchCalendar($id); - + OCP\Util::emitHook('OC_Calendar', 'addEvent', $object_id); return $object_id; } @@ -149,6 +170,7 @@ class OC_Calendar_Object{ $stmt->execute(array($type,$startdate,$enddate,$repeating,$summary,$data,time(),$id)); OC_Calendar_Calendar::touchCalendar($oldobject['calendarid']); + OCP\Util::emitHook('OC_Calendar', 'editEvent', $id); return true; } @@ -170,6 +192,7 @@ class OC_Calendar_Object{ $stmt->execute(array($type,$startdate,$enddate,$repeating,$summary,$data,time(),$oldobject['id'])); OC_Calendar_Calendar::touchCalendar($oldobject['calendarid']); + OCP\Util::emitHook('OC_Calendar', 'editEvent', $oldobject['id']); return true; } @@ -184,6 +207,7 @@ class OC_Calendar_Object{ $stmt = OCP\DB::prepare( 'DELETE FROM `*PREFIX*calendar_objects` WHERE `id` = ?' ); $stmt->execute(array($id)); OC_Calendar_Calendar::touchCalendar($oldobject['calendarid']); + OCP\Util::emitHook('OC_Calendar', 'deleteEvent', $id); return true; } @@ -195,9 +219,11 @@ class OC_Calendar_Object{ * @return boolean */ public static function deleteFromDAVData($cid,$uri){ + $oldobject = self::findWhereDAVDataIs($cid, $uri); $stmt = OCP\DB::prepare( 'DELETE FROM `*PREFIX*calendar_objects` WHERE `calendarid`= ? AND `uri`=?' ); $stmt->execute(array($cid,$uri)); OC_Calendar_Calendar::touchCalendar($cid); + OCP\Util::emitHook('OC_Calendar', 'deleteEvent', $oldobject['id']); return true; } @@ -207,6 +233,7 @@ class OC_Calendar_Object{ $stmt->execute(array($calendarid,$id)); OC_Calendar_Calendar::touchCalendar($id); + OCP\Util::emitHook('OC_Calendar', 'moveEvent', $id); return true; } @@ -302,12 +329,16 @@ class OC_Calendar_Object{ * This function creates a date string that can be used by MDB2. * Furthermore it converts the time to UTC. */ - protected static function getUTCforMDB($datetime){ + public static function getUTCforMDB($datetime){ return date('Y-m-d H:i', $datetime->format('U') - $datetime->getOffset()); } - - public static function getDTEndFromVEvent($vevent) - { + + /** + * @brief returns the DTEND of an $vevent object + * @param object $vevent vevent object + * @return object + */ + public static function getDTEndFromVEvent($vevent){ if ($vevent->DTEND) { $dtend = $vevent->DTEND; }else{ @@ -331,9 +362,12 @@ class OC_Calendar_Object{ } return $dtend; } - - public static function getRepeatOptions($l10n) - { + + /** + * @brief returns the options for the repeat rule of an repeating event + * @return array - valid inputs for the repeat rule of an repeating event + */ + public static function getRepeatOptions($l10n){ return array( 'doesnotrepeat' => $l10n->t('Does not repeat'), 'daily' => $l10n->t('Daily'), @@ -344,26 +378,35 @@ class OC_Calendar_Object{ 'yearly' => $l10n->t('Yearly') ); } - - public static function getEndOptions($l10n) - { + + /** + * @brief returns the options for the end of an repeating event + * @return array - valid inputs for the end of an repeating events + */ + public static function getEndOptions($l10n){ return array( 'never' => $l10n->t('never'), 'count' => $l10n->t('by occurrences'), 'date' => $l10n->t('by date') ); } - - public static function getMonthOptions($l10n) - { + + /** + * @brief returns the options for an monthly repeating event + * @return array - valid inputs for monthly repeating events + */ + public static function getMonthOptions($l10n){ return array( 'monthday' => $l10n->t('by monthday'), 'weekday' => $l10n->t('by weekday') ); } - - public static function getWeeklyOptions($l10n) - { + + /** + * @brief returns the options for an weekly repeating event + * @return array - valid inputs for weekly repeating events + */ + public static function getWeeklyOptions($l10n){ return array( 'MO' => $l10n->t('Monday'), 'TU' => $l10n->t('Tuesday'), @@ -374,9 +417,12 @@ class OC_Calendar_Object{ 'SU' => $l10n->t('Sunday') ); } - - public static function getWeekofMonth($l10n) - { + + /** + * @brief returns the options for an monthly repeating event which occurs on specific weeks of the month + * @return array - valid inputs for monthly repeating events + */ + public static function getWeekofMonth($l10n){ return array( 'auto' => $l10n->t('events week of month'), '1' => $l10n->t('first'), @@ -387,7 +433,11 @@ class OC_Calendar_Object{ '-1' => $l10n->t('last') ); } - + + /** + * @brief returns the options for an yearly repeating event which occurs on specific days of the year + * @return array - valid inputs for yearly repeating events + */ public static function getByYearDayOptions(){ $return = array(); foreach(range(1,366) as $num){ @@ -395,7 +445,11 @@ class OC_Calendar_Object{ } return $return; } - + + /** + * @brief returns the options for an yearly or monthly repeating event which occurs on specific days of the month + * @return array - valid inputs for yearly or monthly repeating events + */ public static function getByMonthDayOptions(){ $return = array(); foreach(range(1,31) as $num){ @@ -403,7 +457,11 @@ class OC_Calendar_Object{ } return $return; } - + + /** + * @brief returns the options for an yearly repeating event which occurs on specific month of the year + * @return array - valid inputs for yearly repeating events + */ public static function getByMonthOptions($l10n){ return array( '1' => $l10n->t('January'), @@ -420,7 +478,11 @@ class OC_Calendar_Object{ '12' => $l10n->t('December') ); } - + + /** + * @brief returns the options for an yearly repeating event + * @return array - valid inputs for yearly repeating events + */ public static function getYearOptions($l10n){ return array( 'bydate' => $l10n->t('by events date'), @@ -429,13 +491,21 @@ class OC_Calendar_Object{ 'bydaymonth' => $l10n->t('by day and month') ); } - + + /** + * @brief returns the options for an yearly repeating event which occurs on specific week numbers of the year + * @return array - valid inputs for yearly repeating events + */ public static function getByWeekNoOptions(){ return range(1, 52); } - - public static function validateRequest($request) - { + + /** + * @brief validates a request + * @param array $request + * @return mixed (array / boolean) + */ + public static function validateRequest($request){ $errnum = 0; $errarr = array('title'=>'false', 'cal'=>'false', 'from'=>'false', 'fromtime'=>'false', 'to'=>'false', 'totime'=>'false', 'endbeforestart'=>'false'); if($request['title'] == ''){ @@ -582,17 +652,24 @@ class OC_Calendar_Object{ } return false; } - - protected static function checkTime($time) - { + + /** + * @brief validates time + * @param string $time + * @return boolean + */ + protected static function checkTime($time){ list($hours, $minutes) = explode(':', $time); return empty($time) || $hours < 0 || $hours > 24 || $minutes < 0 || $minutes > 60; } - - public static function createVCalendarFromRequest($request) - { + + /** + * @brief creates an VCalendar Object from the request data + * @param array $request + * @return object created $vcalendar + */ public static function createVCalendarFromRequest($request){ $vcalendar = new OC_VObject('VCALENDAR'); $vcalendar->add('PRODID', 'ownCloud Calendar'); $vcalendar->add('VERSION', '2.0'); @@ -605,9 +682,14 @@ class OC_Calendar_Object{ $vevent->setUID(); return self::updateVCalendarFromRequest($request, $vcalendar); } - - public static function updateVCalendarFromRequest($request, $vcalendar) - { + + /** + * @brief updates an VCalendar Object from the request data + * @param array $request + * @param object $vcalendar + * @return object updated $vcalendar + */ + public static function updateVCalendarFromRequest($request, $vcalendar){ $title = $request["title"]; $location = $request["location"]; $categories = $request["categories"]; @@ -774,7 +856,7 @@ class OC_Calendar_Object{ $vevent->setDateTime('DTSTART', $start, Sabre_VObject_Property_DateTime::DATE); $vevent->setDateTime('DTEND', $end, Sabre_VObject_Property_DateTime::DATE); }else{ - $timezone = OCP\Config::getUserValue(OCP\USER::getUser(), 'calendar', 'timezone', date_default_timezone_get()); + $timezone = OC_Calendar_App::getTimezone(); $timezone = new DateTimeZone($timezone); $start = new DateTime($from.' '.$fromtime, $timezone); $end = new DateTime($to.' '.$totime, $timezone); @@ -793,15 +875,63 @@ class OC_Calendar_Object{ return $vcalendar; } - + + /** + * @brief returns the owner of an object + * @param integer $id + * @return string + */ public static function getowner($id){ $event = self::find($id); $cal = OC_Calendar_Calendar::find($event['calendarid']); return $cal['userid']; } + /** + * @brief returns the calendarid of an object + * @param integer $id + * @return integer + */ public static function getCalendarid($id){ $event = self::find($id); return $event['calendarid']; } + + /** + * @brief checks if an object is repeating + * @param integer $id + * @return boolean + */ + public static function isrepeating($id){ + $event = self::find($id); + return ($event['repeating'] == 1)?true:false; + } + + /** + * @brief converts the start_dt and end_dt to a new timezone + * @param object $dtstart + * @param object $dtend + * @param boolean $allday + * @param string $tz + * @return array + */ + public static function generateStartEndDate($dtstart, $dtend, $allday, $tz){ + $start_dt = $dtstart->getDateTime(); + $end_dt = $dtend->getDateTime(); + $return = array(); + if($allday){ + $return['start'] = $start_dt->format('Y-m-d'); + $end_dt->modify('-1 minute'); + while($start_dt >= $end_dt){ + $end_dt->modify('+1 day'); + } + $return['end'] = $end_dt->format('Y-m-d'); + }else{ + $start_dt->setTimezone(new DateTimeZone($tz)); + $end_dt->setTimezone(new DateTimeZone($tz)); + $return['start'] = $start_dt->format('Y-m-d H:i:s'); + $return['end'] = $end_dt->format('Y-m-d H:i:s'); + } + return $return; + } } diff --git a/apps/calendar/lib/repeat.php b/apps/calendar/lib/repeat.php new file mode 100644 index 0000000000..b9fbee8fe0 --- /dev/null +++ b/apps/calendar/lib/repeat.php @@ -0,0 +1,204 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ +/** + * This class manages the caching of repeating events + * Events will be cached for the current year ± 5 years + */ +class OC_Calendar_Repeat{ + /** + * @brief returns the cache of an event + * @param (int) $id - id of the event + * @return (array) + */ + public static function get($id){ + $stmt = OCP\DB::prepare('SELECT * FROM *PREFIX*calendar_repeat WHERE eventid = ?'); + $result = $stmt->execute(array($id)); + $return = array(); + while($row = $result->fetchRow()){ + $return[] = $row; + } + return $return; + } + /** + * @brief returns the cache of an event in a specific peroid + * @param (int) $id - id of the event + * @param (DateTime) $from - start for period in UTC + * @param (DateTime) $until - end for period in UTC + * @return (array) + */ + public static function get_inperiod($id, $from, $until){ + $stmt = OCP\DB::prepare( 'SELECT * FROM *PREFIX*calendar_repeat WHERE eventid = ?' + .' AND ((startdate >= ? AND startdate <= ?)' + .' OR (enddate >= ? AND enddate <= ?))'); + $result = $stmt->execute(array($id, + OC_Calendar_Object::getUTCforMDB($from), OC_Calendar_Object::getUTCforMDB($until), + OC_Calendar_Object::getUTCforMDB($from), OC_Calendar_Object::getUTCforMDB($until))); + $return = array(); + while($row = $result->fetchRow()){ + $return[] = $row; + } + return $return; + } + /** + * @brief returns the cache of all repeating events of a calendar + * @param (int) $id - id of the calendar + * @return (array) + */ + public static function getCalendar($id){ + $stmt = OCP\DB::prepare('SELECT * FROM *PREFIX*calendar_repeat WHERE calid = ?'); + $result = $stmt->execute(array($id)); + $return = array(); + while($row = $result->fetchRow()){ + $return[] = $row; + } + return $return; + } + /** + * @brief returns the cache of all repeating events of a calendar in a specific period + * @param (int) $id - id of the event + * @param (string) $from - start for period in UTC + * @param (string) $until - end for period in UTC + * @return (array) + */ + public static function getCalendar_inperiod($id, $from, $until){ + $stmt = OCP\DB::prepare( 'SELECT * FROM *PREFIX*calendar_repeat WHERE calid = ?' + .' AND ((startdate >= ? AND startdate <= ?)' + .' OR (enddate >= ? AND enddate <= ?))'); + $result = $stmt->execute(array($id, + $from, $until, + $from, $until)); + $return = array(); + while($row = $result->fetchRow()){ + $return[] = $row; + } + return $return; + } + /** + * @brief generates the cache the first time + * @param (int) id - id of the event + * @return (bool) + */ + public static function generate($id){ + $event = OC_Calendar_Object::find($id); + if($event['repeating'] == 0){ + return false; + } + $object = OC_VObject::parse($event['calendardata']); + $start = new DateTime('01-01-' . date('Y') . ' 00:00:00', new DateTimeZone('UTC')); + $start->modify('-5 years'); + $end = new DateTime('31-12-' . date('Y') . ' 23:59:59', new DateTimeZone('UTC')); + $end->modify('+5 years'); + $object->expand($start, $end); + foreach($object->getComponents() as $vevent){ + if(!($vevent instanceof Sabre_VObject_Component_VEvent)){ + continue; + } + $startenddate = OC_Calendar_Object::generateStartEndDate($vevent->DTSTART, OC_Calendar_Object::getDTEndFromVEvent($vevent), ($vevent->DTSTART->getDateType() == Sabre_VObject_Element_DateTime::DATE)?true:false, 'UTC'); + $stmt = OCP\DB::prepare('INSERT INTO *PREFIX*calendar_repeat (eventid,calid,startdate,enddate) VALUES(?,?,?,?)'); + $stmt->execute(array($id,OC_Calendar_Object::getCalendarid($id),$startenddate['start'],$startenddate['end'])); + } + return true; + } + /** + * @brief generates the cache the first time for all repeating event of an calendar + * @param (int) id - id of the calendar + * @return (bool) + */ + public static function generateCalendar($id){ + $allobjects = OC_Calendar_Object::all($id); + foreach($allobjects as $event){ + self::generate($event['id']); + } + return true; + } + /** + * @brief updates an event that is already cached + * @param (int) id - id of the event + * @return (bool) + */ + public static function update($id){ + self::clean($id); + self::generate($id); + return true; + } + /** + * @brief updates all repating events of a calendar that are already cached + * @param (int) id - id of the calendar + * @return (bool) + */ + public static function updateCalendar($id){ + self::cleanCalendar($id); + self::generateCalendar($id); + return true; + } + /** + * @brief checks if an event is already cached + * @param (int) id - id of the event + * @return (bool) + */ + public static function is_cached($id){ + if(count(self::get($id)) != 0){ + return true; + }else{ + return false; + } + } + /** + * @brief checks if an event is already cached in a specific period + * @param (int) id - id of the event + * @param (DateTime) $from - start for period in UTC + * @param (DateTime) $until - end for period in UTC + * @return (bool) + */ + public static function is_cached_inperiod($id, $start, $end){ + if(count(self::get_inperiod($id, $start, $end)) != 0){ + return true; + }else{ + return false; + } + + } + /** + * @brief checks if a whole calendar is already cached + * @param (int) id - id of the calendar + * @return (bool) + */ + public static function is_calendar_cached($id){ + $cachedevents = count(self::getCalendar($id)); + $repeatingevents = 0; + $allevents = OC_Calendar_Object::all($id); + foreach($allevents as $event){ + if($event['repeating'] === 1){ + $repeatingevents++; + } + } + if($cachedevents < $repeatingevents){ + return false; + }else{ + return true; + } + } + /** + * @brief removes the cache of an event + * @param (int) id - id of the event + * @return (bool) + */ + public static function clean($id){ + $stmt = OCP\DB::prepare('DELETE FROM *PREFIX*calendar_repeat WHERE eventid = ?'); + $stmt->execute(array($id)); + } + /** + * @brief removes the cache of all events of a calendar + * @param (int) id - id of the calendar + * @return (bool) + */ + public static function cleanCalendar($id){ + $stmt = OCP\DB::prepare('DELETE FROM *PREFIX*calendar_repeat WHERE calid = ?'); + $stmt->execute(array($id)); + } +} \ No newline at end of file diff --git a/apps/calendar/lib/connector_sabre.php b/apps/calendar/lib/sabre/backend.php similarity index 98% rename from apps/calendar/lib/connector_sabre.php rename to apps/calendar/lib/sabre/backend.php index 8eea06da7e..ac3b26ceb3 100644 --- a/apps/calendar/lib/connector_sabre.php +++ b/apps/calendar/lib/sabre/backend.php @@ -212,6 +212,10 @@ class OC_Connector_Sabre_CalDAV extends Sabre_CalDAV_Backend_Abstract { * @return void */ public function deleteCalendar($calendarId) { + if(preg_match( '=iCal/[1-4]?.*Mac OS X/10.[1-6](.[0-9])?=', $_SERVER['HTTP_USER_AGENT'] )){ + throw new Sabre_DAV_Exception_Forbidden("Action is not possible with OSX 10.6.x", 403); + } + OC_Calendar_Calendar::deleteCalendar($calendarId); } diff --git a/apps/calendar/lib/sabre/calendar.php b/apps/calendar/lib/sabre/calendar.php new file mode 100644 index 0000000000..179be1b281 --- /dev/null +++ b/apps/calendar/lib/sabre/calendar.php @@ -0,0 +1,127 @@ +. + * + */ + +/** + * This class overrides Sabre_CalDAV_Calendar::getACL() to return read/write + * permissions based on user and shared state and it overrides + * Sabre_CalDAV_Calendar::getChild() and Sabre_CalDAV_Calendar::getChildren() + * to instantiate OC_Connector_Sabre_CalDAV_CalendarObjects. +*/ +class OC_Connector_Sabre_CalDAV_Calendar extends Sabre_CalDAV_Calendar { + + /** + * Returns a list of ACE's for this node. + * + * Each ACE has the following properties: + * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are + * currently the only supported privileges + * * 'principal', a url to the principal who owns the node + * * 'protected' (optional), indicating that this ACE is not allowed to + * be updated. + * + * @return array + */ + public function getACL() { + + $readprincipal = $this->getOwner(); + $writeprincipal = $this->getOwner(); + $uid = OC_Calendar_Calendar::extractUserID($this->getOwner()); + + if($uid != OCP\USER::getUser()) { + $sharedCalendar = OCP\Share::getItemSharedWithBySource('calendar', $this->$calendarInfo['id']); + if ($sharedCalendar && ($sharedCalendar['permissions'] & OCP\Share::PERMISSION_READ)) { + $readprincipal = 'principals/' . OCP\USER::getUser(); + } + if ($sharedCalendar && ($sharedCalendar['permissions'] & OCP\Share::PERMISSION_UPDATE)) { + $writeprincipal = 'principals/' . OCP\USER::getUser(); + } + } + + return array( + array( + 'privilege' => '{DAV:}read', + 'principal' => $readprincipal, + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}write', + 'principal' => $writeprincipal, + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}read', + 'principal' => $readprincipal . '/calendar-proxy-write', + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}write', + 'principal' => $writeprincipal . '/calendar-proxy-write', + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}read', + 'principal' => $readprincipal . '/calendar-proxy-read', + 'protected' => true, + ), + array( + 'privilege' => '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}read-free-busy', + 'principal' => '{DAV:}authenticated', + 'protected' => true, + ), + + ); + + } + + /** + * Returns a calendar object + * + * The contained calendar objects are for example Events or Todo's. + * + * @param string $name + * @return Sabre_DAV_ICalendarObject + */ + public function getChild($name) { + + $obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'],$name); + if (!$obj) throw new Sabre_DAV_Exception_NotFound('Calendar object not found'); + return new OC_Connector_Sabre_CalDAV_CalendarObject($this->caldavBackend,$this->calendarInfo,$obj); + + } + + /** + * Returns the full list of calendar objects + * + * @return array + */ + public function getChildren() { + + $objs = $this->caldavBackend->getCalendarObjects($this->calendarInfo['id']); + $children = array(); + foreach($objs as $obj) { + $children[] = new OC_Connector_Sabre_CalDAV_CalendarObject($this->caldavBackend,$this->calendarInfo,$obj); + } + return $children; + + } + +} \ No newline at end of file diff --git a/apps/calendar/lib/sabre/calendarroot.php b/apps/calendar/lib/sabre/calendarroot.php new file mode 100644 index 0000000000..e09731c95b --- /dev/null +++ b/apps/calendar/lib/sabre/calendarroot.php @@ -0,0 +1,45 @@ +. + * + */ + +/** + * This class overrides Sabre_CalDAV_CalendarRootNode::getChildForPrincipal() + * to instantiate OC_Connector_Sabre_CalDAV_UserCalendars. +*/ +class OC_Connector_Sabre_CalDAV_CalendarRoot extends Sabre_CalDAV_CalendarRootNode { + + /** + * This method returns a node for a principal. + * + * The passed array contains principal information, and is guaranteed to + * at least contain a uri item. Other properties may or may not be + * supplied by the authentication backend. + * + * @param array $principal + * @return Sabre_DAV_INode + */ + public function getChildForPrincipal(array $principal) { + + return new OC_Connector_Sabre_CalDAV_UserCalendars($this->principalBackend, $this->caldavBackend, $principal['uri']); + + } + +} \ No newline at end of file diff --git a/apps/calendar/lib/sabre/object.php b/apps/calendar/lib/sabre/object.php new file mode 100644 index 0000000000..25954e6ee5 --- /dev/null +++ b/apps/calendar/lib/sabre/object.php @@ -0,0 +1,87 @@ +. + * + */ + +/** + * This class overrides Sabre_CalDAV_CalendarObject::getACL() + * to return read/write permissions based on user and shared state. +*/ +class OC_Connector_Sabre_CalDAV_CalendarObject extends Sabre_CalDAV_CalendarObject { + + /** + * Returns a list of ACE's for this node. + * + * Each ACE has the following properties: + * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are + * currently the only supported privileges + * * 'principal', a url to the principal who owns the node + * * 'protected' (optional), indicating that this ACE is not allowed to + * be updated. + * + * @return array + */ + public function getACL() { + + $readprincipal = $this->getOwner(); + $writeprincipal = $this->getOwner(); + $uid = OC_Calendar_Calendar::extractUserID($this->getOwner()); + + if($uid != OCP\USER::getUser()) { + $sharedAddressbook = OCP\Share::getItemSharedWithBySource('calendar', $this->$calendarInfo['id']); + if ($sharedAddressbook && ($sharedAddressbook['permissions'] & OCP\Share::PERMISSION_READ)) { + $readprincipal = 'principals/' . OCP\USER::getUser(); + } + if ($sharedAddressbook && ($sharedAddressbook['permissions'] & OCP\Share::PERMISSION_UPDATE)) { + $writeprincipal = 'principals/' . OCP\USER::getUser(); + } + } + + return array( + array( + 'privilege' => '{DAV:}read', + 'principal' => $readprincipal, + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}write', + 'principal' => $writeprincipal, + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}read', + 'principal' => $readprincipal . '/calendar-proxy-write', + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}write', + 'principal' => $writeprincipal . '/calendar-proxy-write', + 'protected' => true, + ), + array( + 'privilege' => '{DAV:}read', + 'principal' => $readprincipal . '/calendar-proxy-read', + 'protected' => true, + ), + ); + + } + +} \ No newline at end of file diff --git a/apps/calendar/lib/sabre/usercalendars.php b/apps/calendar/lib/sabre/usercalendars.php new file mode 100644 index 0000000000..919f6b27e1 --- /dev/null +++ b/apps/calendar/lib/sabre/usercalendars.php @@ -0,0 +1,46 @@ +. + * + */ + +/** + * This class overrides Sabre_CalDAV_UserCalendars::getChildren() + * to instantiate OC_Connector_Sabre_CalDAV_Calendars. +*/ +class OC_Connector_Sabre_CalDAV_UserCalendars extends Sabre_CalDAV_UserCalendars { + + /** + * Returns a list of calendars + * + * @return array + */ + public function getChildren() { + + $calendars = $this->caldavBackend->getCalendarsForUser($this->principalInfo['uri']); + $objs = array(); + foreach($calendars as $calendar) { + $objs[] = new OC_Connector_Sabre_CalDAV_Calendar($this->principalBackend, $this->caldavBackend, $calendar); + } + $objs[] = new Sabre_CalDAV_Schedule_Outbox($this->principalInfo['uri']); + return $objs; + + } + +} \ No newline at end of file diff --git a/apps/calendar/lib/search.php b/apps/calendar/lib/search.php index 8b8b0e0c7e..551489672b 100644 --- a/apps/calendar/lib/search.php +++ b/apps/calendar/lib/search.php @@ -1,7 +1,7 @@ ? " . $permission_where . " " . $active_where); - $result = $stmt->execute(array($userid, $userid)); - $return = array(); - while( $row = $result->fetchRow()){ - $return[] = $row; + $format = OC_Share_Backend_Calendar::FORMAT_CALENDAR; + if ($type == self::EVENT) { + $format = OC_Share_Backend_Event::FORMAT_EVENT; } + $return = OCP\Share::getItemsSharedWith($type, + $format, + array( + 'active' => $active, + 'permissions' => $permission, + )); return $return; } - /* + /** * @brief: returns all users a calendar / event is shared with - * @param: (int) id - id of the calendar / event - * @param: (string) $type - use const self::CALENDAR or self::EVENT - * @return: (array) $users - information about users a calendar / event is shared with + * @param: integer id - id of the calendar / event + * @param: string $type - use const self::CALENDAR or self::EVENT + * @return: array $users - information about users a calendar / event is shared with */ public static function allUsersSharedwith($id, $type){ $stmt = OCP\DB::prepare('SELECT * FROM `*PREFIX*calendar_share_' . $type . '` WHERE `' . $type . 'id` = ? ORDER BY `share`'); @@ -48,13 +45,13 @@ class OC_Calendar_Share{ } return $users; } - /* + /** * @brief: shares a calendar / event - * @param: (string) $owner - userid of the owner - * @param: (string) $share - userid (if $sharetype == user) / groupid (if $sharetype == group) / token (if $sharetype == public) - * @param: (string) $sharetype - type of sharing (can be: user/group/public) - * @param: (string) $id - id of the calendar / event - * @param: (string) $type - use const self::CALENDAR or self::EVENT + * @param: string $owner - userid of the owner + * @param: string $share - userid (if $sharetype == user) / groupid (if $sharetype == group) / token (if $sharetype == public) + * @param: string $sharetype - type of sharing (can be: user/group/public) + * @param: string $id - id of the calendar / event + * @param: string $type - use const self::CALENDAR or self::EVENT * @return (mixed) - token (if $sharetype == public) / bool (if $sharetype != public) */ public static function share($owner, $share, $sharetype, $id, $type){ @@ -80,14 +77,14 @@ class OC_Calendar_Share{ return true; } } - /* + /** * @brief: stops sharing a calendar / event - * @param: (string) $owner - userid of the owner - * @param: (string) $share - userid (if $sharetype == user) / groupid (if $sharetype == group) / token (if $sharetype == public) - * @param: (string) $sharetype - type of sharing (can be: user/group/public) - * @param: (string) $id - id of the calendar / event - * @param: (string) $type - use const self::CALENDAR or self::EVENT - * @return (bool) + * @param: string $owner - userid of the owner + * @param: string $share - userid (if $sharetype == user) / groupid (if $sharetype == group) / token (if $sharetype == public) + * @param: string $sharetype - type of sharing (can be: user/group/public) + * @param: string $id - id of the calendar / event + * @param: string $type - use const self::CALENDAR or self::EVENT + * @return boolean */ public static function unshare($owner, $share, $sharetype, $id, $type){ $stmt = OCP\DB::prepare('DELETE FROM `*PREFIX*calendar_share_' . $type . '` WHERE `owner` = ? ' . (($sharetype != 'public')?'AND `share` = ?':'') . ' AND `sharetype` = ? AND `' . $type . 'id` = ?'); @@ -98,14 +95,14 @@ class OC_Calendar_Share{ } return true; } - /* + /** * @brief: changes the permission for a calendar / event - * @param: (string) $share - userid (if $sharetype == user) / groupid (if $sharetype == group) / token (if $sharetype == public) - * @param: (string) $sharetype - type of sharing (can be: user/group/public) - * @param: (string) $id - id of the calendar / event - * @param: (int) $permission - permission of user the calendar / event is shared with (if $sharetype == public then $permission = 0) - * @param: (string) $type - use const self::CALENDAR or self::EVENT - * @return (bool) + * @param: string $share - userid (if $sharetype == user) / groupid (if $sharetype == group) / token (if $sharetype == public) + * @param: string $sharetype - type of sharing (can be: user/group/public) + * @param: string $id - id of the calendar / event + * @param: integer $permission - permission of user the calendar / event is shared with (if $sharetype == public then $permission = 0) + * @param: string $type - use const self::CALENDAR or self::EVENT + * @return boolean */ public static function changepermission($share, $sharetype, $id, $permission, $type){ if($sharetype == 'public' && $permission == 1){ @@ -115,9 +112,9 @@ class OC_Calendar_Share{ $stmt->execute(array($permission, $share, $sharetype, $id)); return true; } - /* + /** * @brief: generates a token for public calendars / events - * @return: (string) $token + * @return: string $token */ private static function generate_token($id, $type){ $uniqid = uniqid(); @@ -138,14 +135,14 @@ class OC_Calendar_Share{ $token = md5($string); return substr($token, rand(0,16), 15); } - /* + /** * @brief: checks if it is already shared - * @param: (string) $owner - userid of the owner - * @param: (string) $share - userid (if $sharetype == user) / groupid (if $sharetype == group) / token (if $sharetype == public) - * @param: (string) $sharetype - type of sharing (can be: user/group/public) - * @param: (string) $id - id of the calendar / event - * @param: (string) $type - use const self::CALENDAR or self::EVENT - * @return (bool) + * @param: string $owner - userid of the owner + * @param: string $share - userid (if $sharetype == user) / groupid (if $sharetype == group) / token (if $sharetype == public) + * @param: string $sharetype - type of sharing (can be: user/group/public) + * @param: string $id - id of the calendar / event + * @param: string $type - use const self::CALENDAR or self::EVENT + * @return boolean */ public static function is_already_shared($owner, $share, $sharetype, $id, $type){ $stmt = OCP\DB::prepare('SELECT * FROM `*PREFIX*calendar_share_' . $type . '` WHERE `owner` = ? AND `share` = ? AND `sharetype` = ? AND `' . $type . 'id` = ?'); @@ -181,12 +178,12 @@ class OC_Calendar_Share{ } return $active_where; } - /* + /** * @brief: checks the permission for editing an event - * @param: (string) $share - userid (if $sharetype == user) / groupid (if $sharetype == group) / token (if $sharetype == public) - * @param: (string) $id - id of the calendar / event - * @param: (string) $type - use const self::CALENDAR or self::EVENT - * @return (bool) + * @param: string $share - userid (if $sharetype == user) / groupid (if $sharetype == group) / token (if $sharetype == public) + * @param: string $id - id of the calendar / event + * @param: string $type - use const self::CALENDAR or self::EVENT + * @return boolean */ public static function is_editing_allowed($share, $id, $type){ $group_where = self::group_sql(OC_Group::getUserGroups($share)); @@ -202,16 +199,16 @@ class OC_Calendar_Share{ } return false; } - /* + /** * @brief: checks the access of - * @param: (string) $share - userid (if $sharetype == user) / groupid (if $sharetype == group) / token (if $sharetype == public) - * @param: (string) $id - id of the calendar / event - * @param: (string) $type - use const self::CALENDAR or self::EVENT - * @return (bool) + * @param: string $share - userid (if $sharetype == user) / groupid (if $sharetype == group) / token (if $sharetype == public) + * @param: string $id - id of the calendar / event + * @param: string $type - use const self::CALENDAR or self::EVENT + * @return boolean */ public static function check_access($share, $id, $type){ $group_where = self::group_sql(OC_Group::getUserGroups($share)); - $stmt = OCP\DB::prepare('SELECT * FROM `*PREFIX*calendar_share`_' . $type . '` WHERE (`' . $type . 'id` = ? AND (`share` = ? AND `sharetype` = \'user\') ' . $group_where . ')'); + $stmt = OCP\DB::prepare('SELECT * FROM `*PREFIX*calendar_share_' . $type . '` WHERE (`' . $type . 'id` = ? AND (`share` = ? AND `sharetype` = \'user\') ' . $group_where . ')'); $result = $stmt->execute(array($id,$share)); $rows = $result->numRows(); if($rows > 0){ @@ -223,9 +220,9 @@ class OC_Calendar_Share{ return false; } } - /* + /** * @brief: returns the calendardata of an event or a calendar - * @param: (string) $token - token which should be searched + * @param: string $token - token which should be searched * @return: mixed - bool if false, array with type and id if true */ public static function getElementByToken($token){ @@ -248,19 +245,19 @@ class OC_Calendar_Share{ return $return; } - /* + /** * @brief sets the active status of the calendar - * @param (string) $ + * @param string */ public static function set_active($share, $id, $active){ $stmt = OCP\DB::prepare("UPDATE `*PREFIX*calendar_share_calendar` SET `active` = ? WHERE `share` = ? AND `sharetype` = 'user' AND `calendarid` = ?"); $stmt->execute(array($active, $share, $id)); } - /* - * @brief delete all shared calendars / events after a user was deleted - * @param (string) $userid - * @return (bool) + /** + * @brief deletes all shared calendars / events after a user was deleted + * @param string $userid + * @return boolean */ public static function post_userdelete($userid){ $stmt = OCP\DB::prepare('DELETE FROM `*PREFIX*calendar_share_calendar` WHERE `owner` = ?'); @@ -273,4 +270,26 @@ class OC_Calendar_Share{ $stmt->execute(array($userid)); return true; } + + /** + * @brief deletes all shared events of a calendar + * @param integer $calid + * @return boolean + */ + public static function post_caldelete($calid){ + $stmt = OCP\DB::prepare('DELETE FROM `*PREFIX*calendar_share_calendar` WHERE `calendarid` = ?'); + $stmt->execute(array($calid)); + return true; + } + + /** + * @brief deletes all shares of an event + * @param integer $eventid + * @return boolean + */ + public static function post_eventdelete($eventid){ + $stmt = OCP\DB::prepare('DELETE FROM `*PREFIX*calendar_share_event` WHERE `eventid` = ?'); + $stmt->execute(array($eventid)); + return true; + } } \ No newline at end of file diff --git a/apps/calendar/lib/share/calendar.php b/apps/calendar/lib/share/calendar.php new file mode 100644 index 0000000000..7f49829241 --- /dev/null +++ b/apps/calendar/lib/share/calendar.php @@ -0,0 +1,111 @@ + +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE +* License as published by the Free Software Foundation; either +* version 3 of the License, or any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU AFFERO GENERAL PUBLIC LICENSE for more details. +* +* You should have received a copy of the GNU Affero General Public +* License along with this library. If not, see . +*/ + +class OC_Share_Backend_Calendar implements OCP\Share_Backend_Collection { + const FORMAT_CALENDAR = 1; + + /** + * @brief Get the source of the item to be stored in the database + * @param string Item + * @param string Owner of the item + * @return mixed|array|false Source + * + * Return an array if the item is file dependent, the array needs two keys: 'item' and 'file' + * Return false if the item does not exist for the user + * + * The formatItems() function will translate the source returned back into the item + */ + public function isValidSource($itemSource, $uidOwner) { + $calendar = OC_Calendar_App::getCalendar( $itemSource ); + if ($calendar || $calendar['userid'] != $uidOwner) { + return false; + } + return true; + } + + /** + * @brief Get a unique name of the item for the specified user + * @param string Item + * @param string|false User the item is being shared with + * @param array|null List of similar item names already existing as shared items + * @return string Target name + * + * This function needs to verify that the user does not already have an item with this name. + * If it does generate a new name e.g. name_# + */ + public function generateTarget($itemSource, $shareWith, $exclude = null) { + $calendar = OC_Calendar_App::getCalendar( $itemSource ); + $user_calendars = array(); + foreach(OC_Contacts_Addressbook::all($uid) as $user_calendar) { + $user_calendars[] = $user_calendar['displayname']; + } + $name = $calendar['userid']."'s ".$calendar['displayname']; + $suffix = ''; + while (in_array($name.$suffix, $user_calendars)) { + $suffix++; + } + + return $name.$suffix; + } + + /** + * @brief Converts the shared item sources back into the item in the specified format + * @param array Shared items + * @param int Format + * @return ? + * + * The items array is a 3-dimensional array with the item_source as the first key and the share id as the second key to an array with the share info. + * The key/value pairs included in the share info depend on the function originally called: + * If called by getItem(s)Shared: id, item_type, item, item_source, share_type, share_with, permissions, stime, file_source + * If called by getItem(s)SharedWith: id, item_type, item, item_source, item_target, share_type, share_with, permissions, stime, file_source, file_target + * This function allows the backend to control the output of shared items with custom formats. + * It is only called through calls to the public getItem(s)Shared(With) functions. + */ + public function formatItems($items, $format, $parameters = null) { + $calendars = array(); + if ($format == self::FORMAT_CALENDAR) { + foreach ($items as $item) { + $calendar = OC_Calendar_App::getCalendar($item['item_source'], false); + // TODO: really check $parameters['permissions'] == 'rw'/'r' + if ($parameters['permissions'] == 'rw') { + continue; // TODO + } + $calendar['displaynamename'] = $item['item_target']; + $calendar['calendarid'] = $calendar['id']; + $calendar['owner'] = $calendar['userid']; + $calendars[] = $calendar; + } + } + return $calendars; + } + + public function getChildren($itemSource) { + $query = OCP\DB::prepare('SELECT id FROM *PREFIX*calendar_objects WHERE calendarid = ?'); + $result = $query->execute(array($itemSource)); + $sources = array(); + while ($object = $result->fetchRow()) { + $sources[] = $object['id']; + } + return $sources; + } + +} \ No newline at end of file diff --git a/apps/calendar/lib/share/event.php b/apps/calendar/lib/share/event.php new file mode 100644 index 0000000000..5bb72ee6c9 --- /dev/null +++ b/apps/calendar/lib/share/event.php @@ -0,0 +1,40 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +class OC_Share_Backend_Event implements OCP\Share_Backend { + + const FORMAT_EVENT = 0; + + private static $event; + + public function isValidSource($itemSource, $uidOwner) { + self::$event = OC_Calendar_Object::find($itemSource); + if (self::$event) { + return true; + } + return false; + } + + public function generateTarget($itemSource, $shareWith, $exclude = null) { + // TODO Get default calendar and check for conflicts + return self::$event['summary']; + } + + public function formatItems($items, $format, $parameters = null) { + $events = array(); + if ($format == self::FORMAT_EVENT) { + foreach ($items as $item) { + $event = OC_Calendar_Object::find($item['item_source']); + $event['summary'] = $item['item_target']; + $events[] = $event; + } + } + return $events; + } + +} diff --git a/apps/calendar/lib/share_backend.php b/apps/calendar/lib/share_backend.php new file mode 100644 index 0000000000..f937c0d1c3 --- /dev/null +++ b/apps/calendar/lib/share_backend.php @@ -0,0 +1,44 @@ +. +*/ + +class OC_Share_Backend_Calendar extends OCP\Share_Backend { + + public function getSource($item, $uid) { + $query = OCP\DB::prepare('SELECT id FROM *PREFIX*calendar_calendars WHERE userid = ? AND displayname = ? LIMIT 1'); + return $query->execute(array($uid, $item))->fetchAll(); + } + + public function generateTarget($item, $uid) { + + } + + public function getItems($sources) { + + } + +} + +class OC_Share_Backend_Event extends OCP\Share_Backend { + +} + + +?> \ No newline at end of file diff --git a/apps/calendar/settings.php b/apps/calendar/settings.php index a18b1ca9f4..f563518046 100644 --- a/apps/calendar/settings.php +++ b/apps/calendar/settings.php @@ -10,7 +10,8 @@ $tmpl = new OCP\Template( 'calendar', 'settings'); $timezone=OCP\Config::getUserValue(OCP\USER::getUser(),'calendar','timezone',''); $tmpl->assign('timezone',$timezone); $tmpl->assign('timezones',DateTimeZone::listIdentifiers()); +$tmpl->assign('calendars', OC_Calendar_Calendar::allCalendars(OCP\USER::getUser()), false); OCP\Util::addscript('calendar','settings'); -return $tmpl->fetchPage(); +$tmpl->printPage(); \ No newline at end of file diff --git a/apps/calendar/share.php b/apps/calendar/share.php index 68c7d0ffae..bffcf0b470 100644 --- a/apps/calendar/share.php +++ b/apps/calendar/share.php @@ -1,22 +1,31 @@ + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ $token = strip_tags($_GET['t']); $shared = OC_Calendar_Share::getElementByToken($token); -$nl = "\n\r"; if($shared['type'] == OC_Calendar_Share::CALENDAR){ $calendar = OC_Calendar_App::getCalendar($shared['id'], false); - $calobjects = OC_Calendar_Object::all($shared['id']); - header('Content-Type: text/Calendar'); - header('Content-Disposition: inline; filename=' . $calendar['displayname'] . '.ics'); - foreach($calobjects as $calobject){ - echo $calobject['calendardata'] . $nl; + if(!$calendar){ + header('HTTP/1.0 404 Not Found'); + exit; } + header('Content-Type: text/Calendar'); + header('Content-Disposition: inline; filename=' . str_replace(' ', '-', $calendar['displayname']) . '.ics'); + echo OC_Calendar_Export::export($shared['id'], OC_Calendar_Export::CALENDAR); }elseif($shared['type'] == OC_Calendar_Share::EVENT){ $data = OC_Calendar_App::getEventObject($shared['id'], false); - $calendarid = $data['calendarid']; - $calendar = OC_Calendar_App::getCalendar($calendarid); + if(!$data){ + header('HTTP/1.0 404 Not Found'); + exit; + } header('Content-Type: text/Calendar'); - header('Content-Disposition: inline; filename=' . $data['summary'] . '.ics'); - echo $data['calendardata']; + header('Content-Disposition: inline; filename=' . str_replace(' ', '-', $data['summary']) . '.ics'); + echo OC_Calendar_Export::export($shared['id'], OC_Calendar_Export::EVENT); }else{ - header('Error 404: Not Found'); -} \ No newline at end of file + header('HTTP/1.0 404 Not Found'); + exit; +} diff --git a/apps/calendar/templates/calendar.php b/apps/calendar/templates/calendar.php index 832194f0fe..15891aafd9 100644 --- a/apps/calendar/templates/calendar.php +++ b/apps/calendar/templates/calendar.php @@ -2,10 +2,10 @@ var defaultView = ''; var eventSources = ; var categories = ; - var dayNames = tA(array('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'))) ?>; - var dayNamesShort = tA(array('Sun.', 'Mon.', 'Tue.', 'Wed.', 'Thu.', 'Fri.', 'Sat.'))) ?>; - var monthNames = tA(array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'))) ?>; - var monthNamesShort = tA(array('Jan.', 'Feb.', 'Mar.', 'Apr.', 'May.', 'Jun.', 'Jul.', 'Aug.', 'Sep.', 'Oct.', 'Nov.', 'Dec.'))) ?>; + var dayNames = new Array(" t("Sunday");?>", " t("Monday");?>", " t("Tuesday");?>", " t("Wednesday");?>", " t("Thursday");?>", " t("Friday");?>", " t("Saturday");?>"); + var dayNamesShort = new Array(" t("Sun.");?>", " t("Mon.");?>", " t("Tue.");?>", " t("Wed.");?>", " t("Thu.");?>", " t("Fri.");?>", " t("Sat.");?>"); + var monthNames = new Array(" t("January");?>", " t("February");?>", " t("March");?>", " t("April");?>", " t("May");?>", " t("June");?>", " t("July");?>", " t("August");?>", " t("September");?>", " t("October");?>", " t("November");?>", " t("December");?>"); + var monthNamesShort = new Array(" t("Jan.");?>", " t("Feb.");?>", " t("Mar.");?>", " t("Apr.");?>", " t("May.");?>", " t("Jun.");?>", " t("Jul.");?>", " t("Aug.");?>", " t("Sep.");?>", " t("Oct.");?>", " t("Nov.");?>", " t("Dec.");?>"); var agendatime = '{ - }'; var defaulttime = ''; var allDayText = 't('All day')) ?>'; @@ -33,37 +33,25 @@ ?> }); -
    -
    - -
    - - -    - -
    - -
    -
    - "/> - " onclick="Calendar.UI.Calendar.overview();" /> -
    -
    -
    -
    - - - -
    -
    -
    -
    -
    +
    +
    + + +    + +
    +
    + + <?php echo $l->t('Settings'); ?> + <?php echo $l->t('Settings'); ?> +
    +
    + + + +
    - +
    -
    - t("There was a fail, while parsing the file."); ?> -
    - + \ No newline at end of file diff --git a/apps/calendar/templates/part.choosecalendar.php b/apps/calendar/templates/part.choosecalendar.php index 8d621cc363..ad2f9e753f 100644 --- a/apps/calendar/templates/part.choosecalendar.php +++ b/apps/calendar/templates/part.choosecalendar.php @@ -1,51 +1,52 @@ -
    "> -

    t('Your calendars'); ?>:

    - -"; - $tmpl = new OCP\Template('calendar', 'part.choosecalendar.rowfields'); - $tmpl->assign('calendar', $option_calendars[$i]); - if(OC_Calendar_Share::allUsersSharedwith($option_calendars[$i]['id'], OC_Calendar_Share::CALENDAR) == array()){ - $shared = false; - }else{ - $shared = true; + +

    t('Your calendars'); ?>:

    +
    + "; + $tmpl = new OCP\Template('calendar', 'part.choosecalendar.rowfields'); + $tmpl->assign('calendar', $option_calendars[$i]); + if(OC_Calendar_Share::allUsersSharedwith($option_calendars[$i]['id'], OC_Calendar_Share::CALENDAR) == array()){ + $shared = false; + }else{ + $shared = true; + } + $tmpl->assign('shared', $shared); + $tmpl->printpage(); + echo ""; } - $tmpl->assign('shared', $shared); - $tmpl->printpage(); - echo ""; -} -?> - - - - - - -
    - -
    -

    ">

    -

    -

    t('Shared calendars'); ?>:

    - -'; - $tmpl = new OCP\Template('calendar', 'part.choosecalendar.rowfields.shared'); - $tmpl->assign('share', $share[$i]); - $tmpl->printpage(); - echo ''; -} -?> -
    -' . $l->t('No shared calendars') . '

    '; -} -?> -
    \ No newline at end of file + ?> + + + + + + + +

    ">

    + + +
    +

    t('Shared calendars'); ?>:

    + + '; + $tmpl = new OCP\Template('calendar', 'part.choosecalendar.rowfields.shared'); + $tmpl->assign('share', $share[$i]); + $tmpl->printpage(); + echo ''; + } + ?> +
    + ' . $l->t('No shared calendars') . '

    '; + } + ?> + + \ No newline at end of file diff --git a/apps/calendar/templates/part.choosecalendar.rowfields.php b/apps/calendar/templates/part.choosecalendar.rowfields.php index 268c335601..64aaa79719 100644 --- a/apps/calendar/templates/part.choosecalendar.rowfields.php +++ b/apps/calendar/templates/part.choosecalendar.rowfields.php @@ -1,8 +1,21 @@ -'; -echo ''; -echo ''; -echo ''; -echo ''; -echo ''; -echo ''; + + > + + + + + + + + + + + + + + + + + + + diff --git a/apps/calendar/templates/part.editevent.php b/apps/calendar/templates/part.editevent.php index 102366f8f0..ea91192cc6 100644 --- a/apps/calendar/templates/part.editevent.php +++ b/apps/calendar/templates/part.editevent.php @@ -5,9 +5,9 @@ inc("part.eventform"); ?>
    - " onclick="Calendar.UI.validateEventForm('?app=calendar&getfile=ajax/event/edit.php');"> - " onclick="Calendar.UI.submitDeleteEventForm('?app=calendar&getfile=ajax/event/delete.php');"> - " onclick="window.location='?app=calendar&getfile=export.php?eventid=';"> + " onclick="Calendar.UI.validateEventForm('');"> + " onclick="Calendar.UI.submitDeleteEventForm('');"> + " onclick="window.location='?eventid=';">
    diff --git a/apps/calendar/templates/part.eventform.php b/apps/calendar/templates/part.eventform.php index 2d86ce4d31..95eecf2622 100644 --- a/apps/calendar/templates/part.eventform.php +++ b/apps/calendar/templates/part.eventform.php @@ -18,7 +18,7 @@ echo 'Calendar.UI.Share.idtype = "event";' . "\n" . 'Calendar.UI.Share.currentid t("Title");?>: - " value="" maxlength="100" name="title"/> + " value="" maxlength="100" name="title"/> @@ -26,7 +26,7 @@ echo 'Calendar.UI.Share.idtype = "event";' . "\n" . 'Calendar.UI.Share.currentid t("Category");?>: - + <?php echo $l->t('Edit categories'); ?> 1) { ?> @@ -80,7 +80,7 @@ echo 'Calendar.UI.Share.idtype = "event";' . "\n" . 'Calendar.UI.Share.currentid t("Location");?>: - " value="" maxlength="100" name="location" /> + " value="" maxlength="100" name="location" /> @@ -88,7 +88,7 @@ echo 'Calendar.UI.Share.idtype = "event";' . "\n" . 'Calendar.UI.Share.currentid t("Description");?>: - + diff --git a/apps/calendar/templates/part.import.php b/apps/calendar/templates/part.import.php index 3850ddde56..2ce3cc3423 100644 --- a/apps/calendar/templates/part.import.php +++ b/apps/calendar/templates/part.import.php @@ -1,30 +1,58 @@ -
    "> -
    - - - -

    t('Please choose the calendar'); ?>

    - - -!" id="startimport"> -
    - diff --git a/apps/calendar/templates/settings.php b/apps/calendar/templates/settings.php index feb0665512..56a6a42ee0 100644 --- a/apps/calendar/templates/settings.php +++ b/apps/calendar/templates/settings.php @@ -1,17 +1,22 @@ - * Copyright (c) 2011 Georg Ehrke + * Copyright (c) 2012 Georg Ehrke * This file is licensed under the Affero General Public License version 3 or * later. * See the COPYING-README file. */ ?> -
    -
    - t('Calendar'); ?> - - + + + + + +
    + + + - - + + + + + + + + - - + + + + - -
    + +    + +
    + +
    +    + + +   + +
    + +    + -
    +
    + +    + -
    - +
    + +    + + +
    +
    +

    t('URLs'); ?>

    +
    t('Calendar CalDAV syncing addresses'); ?> (t('more info'); ?>)
    t('Primary address (Kontact et al)'); ?>
    t('iOS/OS X'); ?>
    principals//
    +
    t('Read only iCalendar link(s)'); ?>
    +
    + +
    + +
    - - +
    +
    \ No newline at end of file diff --git a/apps/calendar/templates/share.dropdown.php b/apps/calendar/templates/share.dropdown.php index 07b4c4bced..391ae83765 100644 --- a/apps/calendar/templates/share.dropdown.php +++ b/apps/calendar/templates/share.dropdown.php @@ -33,7 +33,7 @@ echo OCP\html_select_options($allusers, array());
      -
    • style="visibility:hidden;" title="t('Editable'); ?>">
    • +
    • style="visibility:hidden;" title="t('Editable'); ?>">
    • -
      -
        - inc("part.contacts"); ?> -
      -
      -
      -
      - - -
      +
      + +
      +
      +
      +
      + + + +
      + + +
      + +
      inc('part.contact'); } else{ echo $this->inc('part.no_contacts'); } ?> +
      + + + + diff --git a/apps/contacts/templates/part.chooseaddressbook.php b/apps/contacts/templates/part.chooseaddressbook.php deleted file mode 100644 index a0ec053ab9..0000000000 --- a/apps/contacts/templates/part.chooseaddressbook.php +++ /dev/null @@ -1,25 +0,0 @@ -
      "> - -"; - $tmpl = new OCP\Template('contacts', 'part.chooseaddressbook.rowfields'); - $tmpl->assign('addressbook', $option_addressbooks[$i]); - $tmpl->assign('active', OC_Contacts_Addressbook::isActive($option_addressbooks[$i]['id'])); - $tmpl->printpage(); - echo ""; -} -?> - - - - - - -
      - t('New Address Book') ?> - t('Import from VCF') ?> -
      -

      ">

      -
      diff --git a/apps/contacts/templates/part.chooseaddressbook.rowfields.php b/apps/contacts/templates/part.chooseaddressbook.rowfields.php deleted file mode 100644 index 780920ea3c..0000000000 --- a/apps/contacts/templates/part.chooseaddressbook.rowfields.php +++ /dev/null @@ -1,18 +0,0 @@ - - " type="checkbox" onClick="Contacts.UI.Addressbooks.activation(this, )" > - - - - - - ');" title="t("CardDav Link"); ?>" class="svg action globe"> - - - " title="t("Download"); ?>" class="svg action download"> - - - " class="svg action edit" onclick="Contacts.UI.Addressbooks.editAddressbook(this, );"> - - - );" title="t("Delete"); ?>" class="svg action delete"> - diff --git a/apps/contacts/templates/part.contact.php b/apps/contacts/templates/part.contact.php index 5b07ba3c33..87ed07c0ec 100644 --- a/apps/contacts/templates/part.contact.php +++ b/apps/contacts/templates/part.contact.php @@ -1,25 +1,34 @@ +
      - +
      -
      +
      +
      -
      + +
      +
      @@ -31,6 +40,8 @@ $id = isset($_['id']) ? $_['id'] : ''; + + @@ -39,73 +50,88 @@ $id = isset($_['id']) ? $_['id'] : '';
      -
      - -
      -
      - -
      -
        - -
      -
      + + - -
      -
        - -
      -
      + + - - -
      -
      + + -
      -
      +
      + +
      @@ -117,19 +143,3 @@ $id = isset($_['id']) ? $_['id'] : '';
      - diff --git a/apps/contacts/templates/part.contactphoto.php b/apps/contacts/templates/part.contactphoto.php deleted file mode 100644 index bddf4cc8a8..0000000000 --- a/apps/contacts/templates/part.contactphoto.php +++ /dev/null @@ -1,16 +0,0 @@ - - - src="?id=" /> - - - diff --git a/apps/contacts/templates/part.contacts.php b/apps/contacts/templates/part.contacts.php deleted file mode 100644 index 5751750540..0000000000 --- a/apps/contacts/templates/part.contacts.php +++ /dev/null @@ -1,12 +0,0 @@ - -
    • - diff --git a/apps/contacts/templates/part.cropphoto.php b/apps/contacts/templates/part.cropphoto.php index e107217913..3f5817622b 100644 --- a/apps/contacts/templates/part.cropphoto.php +++ b/apps/contacts/templates/part.cropphoto.php @@ -1,10 +1,9 @@ - - + +
      - - + +
      @@ -60,5 +60,8 @@ OCP\Util::writeLog('contacts','templates/part.cropphoto.php: tmp_path: '.$tmp_pa
      - - +t('The temporary image has been removed from cache.'); +} +?> diff --git a/apps/contacts/templates/part.edit_address_dialog.php b/apps/contacts/templates/part.edit_address_dialog.php index 8b3425033c..81e24ba4d0 100644 --- a/apps/contacts/templates/part.edit_address_dialog.php +++ b/apps/contacts/templates/part.edit_address_dialog.php @@ -1,13 +1,9 @@
      -
      @@ -22,46 +18,46 @@ foreach(isset($adr['parameters']['TYPE'])?array($adr['parameters']['TYPE']):arra
      - +
      +
      + +
      + +
      - -
      -
      - -
      -
      - +
      - +
      - +
      - +
      - +
      +
      Powered by geonames.org
      diff --git a/apps/contacts/templates/part.edit_categories_dialog.php b/apps/contacts/templates/part.edit_categories_dialog.php deleted file mode 100644 index 8997fa586b..0000000000 --- a/apps/contacts/templates/part.edit_categories_dialog.php +++ /dev/null @@ -1,16 +0,0 @@ - -
      - -
      -
      -
        - -
      • - -
      -
      -
      -
      -
      diff --git a/apps/contacts/templates/part.edit_name_dialog.php b/apps/contacts/templates/part.edit_name_dialog.php index be45f9a5b0..f984c232a3 100644 --- a/apps/contacts/templates/part.edit_name_dialog.php +++ b/apps/contacts/templates/part.edit_name_dialog.php @@ -22,7 +22,7 @@ $addressbooks = isset($_['addressbooks'])?$_['addressbooks']:null;
      - +
      -
      +
      -
      +
      -
      +
      - +
    "; +// OC_Mount_Config::addMountPoint('Photos', 'OC_Filestorage_SWIFT', array('host' => 'gapinthecloud.com', 'user' => 'Gap', 'token' => '23423afdasFJEW22', 'secure' => 'true', 'root' => ''), OC_Mount_Config::MOUNT_TYPE_GROUP, 'admin', false); +?> diff --git a/apps/files_external/tests/webdav.php b/apps/files_external/tests/webdav.php index 144659819b..14abbef2cb 100644 --- a/apps/files_external/tests/webdav.php +++ b/apps/files_external/tests/webdav.php @@ -13,7 +13,6 @@ if(!is_array($config) or !isset($config['webdav']) or !$config['webdav']['run']) }else{ class Test_Filestorage_DAV extends Test_FileStorage { private $config; - private $id; public function setUp(){ $id=uniqid(); diff --git a/apps/files_imageviewer/appinfo/app.php b/apps/files_imageviewer/appinfo/app.php index 6c8d8c30ca..6184585cff 100644 --- a/apps/files_imageviewer/appinfo/app.php +++ b/apps/files_imageviewer/appinfo/app.php @@ -4,5 +4,3 @@ OCP\Util::addscript( 'files_imageviewer', 'lightbox' ); OCP\Util::addscript('files_imageviewer', 'jquery.mousewheel-3.0.4.pack'); OCP\Util::addscript('files_imageviewer', 'jquery.fancybox-1.3.4.pack'); OCP\Util::addStyle( 'files_imageviewer', 'jquery.fancybox-1.3.4' ); - -?> diff --git a/apps/files_imageviewer/css/jquery.fancybox-1.3.4.css b/apps/files_imageviewer/css/jquery.fancybox-1.3.4.css index 5fdf7af14c..6e982805a4 100644 --- a/apps/files_imageviewer/css/jquery.fancybox-1.3.4.css +++ b/apps/files_imageviewer/css/jquery.fancybox-1.3.4.css @@ -35,7 +35,7 @@ left: 0; width: 40px; height: 480px; - background-image: url('%appswebroot%/apps/files_imageviewer/img/fancybox.png'); + background-image: url('%appswebroot%/files_imageviewer/img/fancybox.png'); } #fancybox-overlay { @@ -99,7 +99,7 @@ right: -15px; width: 30px; height: 30px; - background: transparent url('%appswebroot%/apps/files_imageviewer/img/fancybox.png') -40px 0px; + background: transparent url('%appswebroot%/files_imageviewer/img/fancybox.png') -40px 0px; cursor: pointer; z-index: 1103; display: none; @@ -137,7 +137,7 @@ width: 35%; cursor: pointer; outline: none; - background: transparent url('%appswebroot%/apps/files_imageviewer/img/blank.gif'); + background: transparent url('%appswebroot%/files_imageviewer/img/blank.gif'); z-index: 1102; display: none; } @@ -163,12 +163,12 @@ } #fancybox-left-ico { - background-image: url('%appswebroot%/apps/files_imageviewer/img/fancybox.png'); + background-image: url('%appswebroot%/files_imageviewer/img/fancybox.png'); background-position: -40px -30px; } #fancybox-right-ico { - background-image: url('%appswebroot%/apps/files_imageviewer/img/fancybox.png'); + background-image: url('%appswebroot%/files_imageviewer/img/fancybox.png'); background-position: -40px -60px; } @@ -199,13 +199,13 @@ top: -20px; left: 0; width: 100%; - background-image: url('%appswebroot%/apps/files_imageviewer/img/fancybox-x.png'); + background-image: url('%appswebroot%/files_imageviewer/img/fancybox-x.png'); } #fancybox-bg-ne { top: -20px; right: -20px; - background-image: url('%appswebroot%/apps/files_imageviewer/img/fancybox.png'); + background-image: url('%appswebroot%/files_imageviewer/img/fancybox.png'); background-position: -40px -162px; } @@ -213,14 +213,14 @@ top: 0; right: -20px; height: 100%; - background-image: url('%appswebroot%/apps/files_imageviewer/img/fancybox-y.png'); + background-image: url('%appswebroot%/files_imageviewer/img/fancybox-y.png'); background-position: -20px 0px; } #fancybox-bg-se { bottom: -20px; right: -20px; - background-image: url('%appswebroot%/apps/files_imageviewer/img/fancybox.png'); + background-image: url('%appswebroot%/files_imageviewer/img/fancybox.png'); background-position: -40px -182px; } @@ -228,14 +228,14 @@ bottom: -20px; left: 0; width: 100%; - background-image: url('%appswebroot%/apps/files_imageviewer/img/fancybox-x.png'); + background-image: url('%appswebroot%/files_imageviewer/img/fancybox-x.png'); background-position: 0px -20px; } #fancybox-bg-sw { bottom: -20px; left: -20px; - background-image: url('%appswebroot%/apps/files_imageviewer/img/fancybox.png'); + background-image: url('%appswebroot%/files_imageviewer/img/fancybox.png'); background-position: -40px -142px; } @@ -243,13 +243,13 @@ top: 0; left: -20px; height: 100%; - background-image: url('%appswebroot%/apps/files_imageviewer/img/fancybox-y.png'); + background-image: url('%appswebroot%/files_imageviewer/img/fancybox-y.png'); } #fancybox-bg-nw { top: -20px; left: -20px; - background-image: url('%appswebroot%/apps/files_imageviewer/img/fancybox.png'); + background-image: url('%appswebroot%/files_imageviewer/img/fancybox.png'); background-position: -40px -122px; } @@ -282,7 +282,7 @@ #fancybox-title-over { padding: 10px; - background-image: url('%appswebroot%/apps/files_imageviewer/img/fancy_title_over.png'); + background-image: url('%appswebroot%/files_imageviewer/img/fancy_title_over.png'); display: block; } @@ -306,7 +306,7 @@ #fancybox-title-float-left { padding: 0 0 0 15px; - background: url('%appswebroot%/apps/files_imageviewer/img/fancybox.png') -40px -90px no-repeat; + background: url('%appswebroot%/files_imageviewer/img/fancybox.png') -40px -90px no-repeat; } #fancybox-title-float-main { @@ -314,25 +314,25 @@ line-height: 29px; font-weight: bold; padding: 0 0 3px 0; - background: url('%appswebroot%/apps/files_imageviewer/img/fancybox-x.png') 0px -40px; + background: url('%appswebroot%/files_imageviewer/img/fancybox-x.png') 0px -40px; } #fancybox-title-float-right { padding: 0 0 0 15px; - background: url('%appswebroot%/apps/files_imageviewer/img/fancybox.png') -55px -90px no-repeat; + background: url('%appswebroot%/files_imageviewer/img/fancybox.png') -55px -90px no-repeat; } /* IE6 */ -.fancybox-ie6 #fancybox-close { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/apps/files_imageviewer/img/fancy_close.png', sizingMethod='scale'); } +.fancybox-ie6 #fancybox-close { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/files_imageviewer/img/fancy_close.png', sizingMethod='scale'); } -.fancybox-ie6 #fancybox-left-ico { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/apps/files_imageviewer/img/fancy_nav_left.png', sizingMethod='scale'); } -.fancybox-ie6 #fancybox-right-ico { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/apps/files_imageviewer/img/fancy_nav_right.png', sizingMethod='scale'); } +.fancybox-ie6 #fancybox-left-ico { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/files_imageviewer/img/fancy_nav_left.png', sizingMethod='scale'); } +.fancybox-ie6 #fancybox-right-ico { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/files_imageviewer/img/fancy_nav_right.png', sizingMethod='scale'); } -.fancybox-ie6 #fancybox-title-over { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/apps/files_imageviewer/img/fancy_title_over.png', sizingMethod='scale'); zoom: 1; } -.fancybox-ie6 #fancybox-title-float-left { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/apps/files_imageviewer/img/fancy_title_left.png', sizingMethod='scale'); } -.fancybox-ie6 #fancybox-title-float-main { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/apps/files_imageviewer/img/fancy_title_main.png', sizingMethod='scale'); } -.fancybox-ie6 #fancybox-title-float-right { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/apps/files_imageviewer/img/fancy_title_right.png', sizingMethod='scale'); } +.fancybox-ie6 #fancybox-title-over { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/files_imageviewer/img/fancy_title_over.png', sizingMethod='scale'); zoom: 1; } +.fancybox-ie6 #fancybox-title-float-left { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/files_imageviewer/img/fancy_title_left.png', sizingMethod='scale'); } +.fancybox-ie6 #fancybox-title-float-main { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/files_imageviewer/img/fancy_title_main.png', sizingMethod='scale'); } +.fancybox-ie6 #fancybox-title-float-right { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/files_imageviewer/img/fancy_title_right.png', sizingMethod='scale'); } .fancybox-ie6 #fancybox-bg-w, .fancybox-ie6 #fancybox-bg-e, .fancybox-ie6 #fancybox-left, .fancybox-ie6 #fancybox-right, #fancybox-hide-sel-frame { height: expression(this.parentNode.clientHeight + "px"); @@ -343,17 +343,17 @@ top: expression( (-20 + (document.documentElement.clientHeight ? document.documentElement.clientHeight/2 : document.body.clientHeight/2 ) + ( ignoreMe = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop )) + 'px'); } -#fancybox-loading.fancybox-ie6 div { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/apps/files_imageviewer/img/fancy_loading.png', sizingMethod='scale'); } +#fancybox-loading.fancybox-ie6 div { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/files_imageviewer/img/fancy_loading.png', sizingMethod='scale'); } /* IE6, IE7, IE8 */ .fancybox-ie .fancybox-bg { background: transparent !important; } -.fancybox-ie #fancybox-bg-n { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/apps/files_imageviewer/img/fancy_shadow_n.png', sizingMethod='scale'); } -.fancybox-ie #fancybox-bg-ne { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/apps/files_imageviewer/img/fancy_shadow_ne.png', sizingMethod='scale'); } -.fancybox-ie #fancybox-bg-e { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/apps/files_imageviewer/img/fancy_shadow_e.png', sizingMethod='scale'); } -.fancybox-ie #fancybox-bg-se { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/apps/files_imageviewer/img/fancy_shadow_se.png', sizingMethod='scale'); } -.fancybox-ie #fancybox-bg-s { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/apps/files_imageviewer/img/fancy_shadow_s.png', sizingMethod='scale'); } -.fancybox-ie #fancybox-bg-sw { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/apps/files_imageviewer/img/fancy_shadow_sw.png', sizingMethod='scale'); } -.fancybox-ie #fancybox-bg-w { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/apps/files_imageviewer/img/fancy_shadow_w.png', sizingMethod='scale'); } -.fancybox-ie #fancybox-bg-nw { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/apps/files_imageviewer/img/fancy_shadow_nw.png', sizingMethod='scale'); } +.fancybox-ie #fancybox-bg-n { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/files_imageviewer/img/fancy_shadow_n.png', sizingMethod='scale'); } +.fancybox-ie #fancybox-bg-ne { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/files_imageviewer/img/fancy_shadow_ne.png', sizingMethod='scale'); } +.fancybox-ie #fancybox-bg-e { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/files_imageviewer/img/fancy_shadow_e.png', sizingMethod='scale'); } +.fancybox-ie #fancybox-bg-se { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/files_imageviewer/img/fancy_shadow_se.png', sizingMethod='scale'); } +.fancybox-ie #fancybox-bg-s { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/files_imageviewer/img/fancy_shadow_s.png', sizingMethod='scale'); } +.fancybox-ie #fancybox-bg-sw { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/files_imageviewer/img/fancy_shadow_sw.png', sizingMethod='scale'); } +.fancybox-ie #fancybox-bg-w { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/files_imageviewer/img/fancy_shadow_w.png', sizingMethod='scale'); } +.fancybox-ie #fancybox-bg-nw { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='%appswebroot%/files_imageviewer/img/fancy_shadow_nw.png', sizingMethod='scale'); } diff --git a/apps/files_imageviewer/img/fancybox-y.png b/apps/files_imageviewer/img/fancybox-y.png index 7ef399b990..62f3176199 100644 Binary files a/apps/files_imageviewer/img/fancybox-y.png and b/apps/files_imageviewer/img/fancybox-y.png differ diff --git a/apps/files_imageviewer/js/lightbox.js b/apps/files_imageviewer/js/lightbox.js index 31f08456d2..ff12d808bc 100644 --- a/apps/files_imageviewer/js/lightbox.js +++ b/apps/files_imageviewer/js/lightbox.js @@ -1,6 +1,6 @@ $(document).ready(function() { if(typeof FileActions!=='undefined'){ - FileActions.register('image','View','',function(filename){ + FileActions.register('image','View', FileActions.PERMISSION_READ, '',function(filename){ viewImage($('#dir').val(),filename); }); FileActions.setDefault('image','View'); diff --git a/apps/files_pdfviewer/appinfo/app.php b/apps/files_pdfviewer/appinfo/app.php index 06b1567067..e4771ee517 100644 --- a/apps/files_pdfviewer/appinfo/app.php +++ b/apps/files_pdfviewer/appinfo/app.php @@ -1,7 +1,8 @@ +OCP\Util::addscript( 'files_pdfviewer', 'pdfjs/viewer'); diff --git a/apps/files_pdfviewer/appinfo/info.xml b/apps/files_pdfviewer/appinfo/info.xml index e3813be100..074962f57f 100644 --- a/apps/files_pdfviewer/appinfo/info.xml +++ b/apps/files_pdfviewer/appinfo/info.xml @@ -4,7 +4,7 @@ PDF Viewer Inline PDF viewer (pdfjs-based) GPL - Joan Creus + Joan Creus, Thomas Müller 4 true diff --git a/apps/files_pdfviewer/css/viewer.css b/apps/files_pdfviewer/css/viewer.css index b735dbfedf..f9ce929d8b 100644 --- a/apps/files_pdfviewer/css/viewer.css +++ b/apps/files_pdfviewer/css/viewer.css @@ -2,7 +2,6 @@ /* vim: set shiftwidth=4 tabstop=8 autoindent cindent expandtab: */ #viewer { - background-color: #929292; font-family: 'Lucida Grande', 'Lucida Sans Unicode', Helvetica, Arial, Verdana, sans-serif; /*margin: 0px;*/ padding: 0px; diff --git a/apps/files_pdfviewer/js/pdfjs/build/pdf.js b/apps/files_pdfviewer/js/pdfjs/build/pdf.js index a19a9b75fe..b1fc9d9747 100644 --- a/apps/files_pdfviewer/js/pdfjs/build/pdf.js +++ b/apps/files_pdfviewer/js/pdfjs/build/pdf.js @@ -7,10 +7,9 @@ var PDFJS = {}; // Use strict in our context only - users might not want it 'use strict'; - PDFJS.build = 'd823592'; + PDFJS.build = '2aae4fd'; // Files are inserted below - see Makefile - /* PDFJSSCRIPT_INCLUDE_ALL */ /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ @@ -20,7 +19,7 @@ var globalScope = (typeof window === 'undefined') ? this : window; var isWorker = (typeof window == 'undefined'); -var ERRORS = 0, WARNINGS = 1, TODOS = 5; +var ERRORS = 0, WARNINGS = 1, INFOS = 5; var verbosity = WARNINGS; // The global PDFJS object exposes the API @@ -44,7 +43,19 @@ function getPdf(arg, callback) { params = { url: arg }; var xhr = new XMLHttpRequest(); + xhr.open('GET', params.url); + + var headers = params.headers; + if (headers) { + for (var property in headers) { + if (typeof headers[property] === 'undefined') + continue; + + xhr.setRequestHeader(property, params.headers[property]); + } + } + xhr.mozResponseType = xhr.responseType = 'arraybuffer'; var protocol = params.url.indexOf(':') < 0 ? window.location.protocol : params.url.substring(0, params.url.indexOf(':') + 1); @@ -76,8 +87,6 @@ var Page = (function PageClosure() { function Page(xref, pageNumber, pageDict, ref) { this.pageNumber = pageNumber; this.pageDict = pageDict; - this.stats = new StatTimer(); - this.stats.enabled = !!globalScope.PDFJS.enableStats; this.xref = xref; this.ref = ref; @@ -113,18 +122,10 @@ var Page = (function PageClosure() { return shadow(this, 'mediaBox', obj); }, get view() { - var cropBox = this.inheritPageProp('CropBox'); - var view = { - x: 0, - y: 0, - width: this.width, - height: this.height - }; - if (!isArray(cropBox) || cropBox.length !== 4) - return shadow(this, 'view', view); - var mediaBox = this.mediaBox; - var offsetX = mediaBox[0], offsetY = mediaBox[1]; + var cropBox = this.inheritPageProp('CropBox'); + if (!isArray(cropBox) || cropBox.length !== 4) + return shadow(this, 'view', mediaBox); // From the spec, 6th ed., p.963: // "The crop, bleed, trim, and art boxes should not ordinarily @@ -132,42 +133,13 @@ var Page = (function PageClosure() { // effectively reduced to their intersection with the media box." cropBox = Util.intersect(cropBox, mediaBox); if (!cropBox) - return shadow(this, 'view', view); + return shadow(this, 'view', mediaBox); - var tl = this.rotatePoint(cropBox[0] - offsetX, cropBox[1] - offsetY); - var br = this.rotatePoint(cropBox[2] - offsetX, cropBox[3] - offsetY); - view.x = Math.min(tl.x, br.x); - view.y = Math.min(tl.y, br.y); - view.width = Math.abs(tl.x - br.x); - view.height = Math.abs(tl.y - br.y); - - return shadow(this, 'view', view); + return shadow(this, 'view', cropBox); }, get annotations() { return shadow(this, 'annotations', this.inheritPageProp('Annots')); }, - get width() { - var mediaBox = this.mediaBox; - var rotate = this.rotate; - var width; - if (rotate == 0 || rotate == 180) { - width = (mediaBox[2] - mediaBox[0]); - } else { - width = (mediaBox[3] - mediaBox[1]); - } - return shadow(this, 'width', width); - }, - get height() { - var mediaBox = this.mediaBox; - var rotate = this.rotate; - var height; - if (rotate == 0 || rotate == 180) { - height = (mediaBox[3] - mediaBox[1]); - } else { - height = (mediaBox[2] - mediaBox[0]); - } - return shadow(this, 'height', height); - }, get rotate() { var rotate = this.inheritPageProp('Rotate') || 0; // Normalize rotation so it's a multiple of 90 and between 0 and 270 @@ -183,43 +155,20 @@ var Page = (function PageClosure() { return shadow(this, 'rotate', rotate); }, - startRenderingFromOperatorList: - function Page_startRenderingFromOperatorList(operatorList, fonts) { - var self = this; - this.operatorList = operatorList; - - var displayContinuation = function pageDisplayContinuation() { - // Always defer call to display() to work around bug in - // Firefox error reporting from XHR callbacks. - setTimeout(function pageSetTimeout() { - self.displayReadyPromise.resolve(); - }); - }; - - this.ensureFonts(fonts, - function pageStartRenderingFromOperatorListEnsureFonts() { - displayContinuation(); - } - ); - }, - getOperatorList: function Page_getOperatorList(handler, dependency) { - if (this.operatorList) { - // content was compiled - return this.operatorList; - } - - this.stats.time('Build IR Queue'); - var xref = this.xref; var content = this.content; var resources = this.resources; if (isArray(content)) { // fetching items + var streams = []; var i, n = content.length; + var streams = []; for (i = 0; i < n; ++i) - content[i] = xref.fetchIfRef(content[i]); - content = new StreamsSequenceStream(content); + streams.push(xref.fetchIfRef(content[i])); + content = new StreamsSequenceStream(streams); + } else if (isStream(content)) { + content.reset(); } else if (!content) { // replacing non-existent page content with empty one content = new Stream(new Uint8Array(0)); @@ -228,9 +177,31 @@ var Page = (function PageClosure() { var pe = this.pe = new PartialEvaluator( xref, handler, 'p' + this.pageNumber + '_'); - this.operatorList = pe.getOperatorList(content, resources, dependency); - this.stats.timeEnd('Build IR Queue'); - return this.operatorList; + return pe.getOperatorList(content, resources, dependency); + }, + extractTextContent: function Page_extractTextContent() { + var handler = { + on: function nullHandlerOn() {}, + send: function nullHandlerSend() {} + }; + + var xref = this.xref; + var content = xref.fetchIfRef(this.content); + var resources = xref.fetchIfRef(this.resources); + if (isArray(content)) { + // fetching items + var i, n = content.length; + var streams = []; + for (i = 0; i < n; ++i) + streams.push(xref.fetchIfRef(content[i])); + content = new StreamsSequenceStream(streams); + } else if (isStream(content)) { + content.reset(); + } + + var pe = new PartialEvaluator( + xref, handler, 'p' + this.pageNumber + '_'); + return pe.getTextContent(content, resources); }, ensureFonts: function Page_ensureFonts(fonts, callback) { @@ -250,60 +221,6 @@ var Page = (function PageClosure() { }.bind(this) ); }, - - display: function Page_display(gfx, callback) { - var stats = this.stats; - stats.time('Rendering'); - var xref = this.xref; - var resources = this.resources; - var mediaBox = this.mediaBox; - assertWellFormed(isDict(resources), 'invalid page resources'); - - gfx.xref = xref; - gfx.res = resources; - gfx.beginDrawing({ x: mediaBox[0], y: mediaBox[1], - width: this.width, - height: this.height, - rotate: this.rotate }); - - var startIdx = 0; - var length = this.operatorList.fnArray.length; - var operatorList = this.operatorList; - var stepper = null; - if (PDFJS.pdfBug && StepperManager.enabled) { - stepper = StepperManager.create(this.pageNumber); - stepper.init(operatorList); - stepper.nextBreakPoint = stepper.getNextBreakPoint(); - } - - var self = this; - function next() { - startIdx = - gfx.executeOperatorList(operatorList, startIdx, next, stepper); - if (startIdx == length) { - gfx.endDrawing(); - stats.timeEnd('Rendering'); - stats.timeEnd('Overall'); - if (callback) callback(); - } - } - next(); - }, - rotatePoint: function Page_rotatePoint(x, y, reverse) { - var rotate = reverse ? (360 - this.rotate) : this.rotate; - switch (rotate) { - case 180: - return {x: this.width - x, y: y}; - case 90: - return {x: this.width - y, y: this.height - x}; - case 270: - return {x: y, y: x}; - case 360: - case 0: - default: - return {x: x, y: this.height - y}; - } - }, getLinks: function Page_getLinks() { var links = []; var annotations = pageGetAnnotations(); @@ -337,6 +254,7 @@ var Page = (function PageClosure() { case 'http': case 'https': case 'ftp': + case 'mailto': return true; default: return false; @@ -355,15 +273,10 @@ var Page = (function PageClosure() { if (!isName(subtype)) continue; var rect = annotation.get('Rect'); - var topLeftCorner = this.rotatePoint(rect[0], rect[1]); - var bottomRightCorner = this.rotatePoint(rect[2], rect[3]); var item = {}; item.type = subtype.name; - item.x = Math.min(topLeftCorner.x, bottomRightCorner.x); - item.y = Math.min(topLeftCorner.y, bottomRightCorner.y); - item.width = Math.abs(topLeftCorner.x - bottomRightCorner.x); - item.height = Math.abs(topLeftCorner.y - bottomRightCorner.y); + item.rect = rect; switch (subtype.name) { case 'Link': var a = annotation.get('A'); @@ -437,7 +350,8 @@ var Page = (function PageClosure() { var title = annotation.get('T'); item.content = stringToPDFString(content || ''); item.title = stringToPDFString(title || ''); - item.name = annotation.get('Name').name; + item.name = !annotation.has('Name') ? 'Note' : + annotation.get('Name').name; break; default: TODO('unimplemented annotation type: ' + subtype.name); @@ -446,37 +360,6 @@ var Page = (function PageClosure() { items.push(item); } return items; - }, - startRendering: function Page_startRendering(ctx, callback, textLayer) { - var stats = this.stats; - stats.time('Overall'); - // If there is no displayReadyPromise yet, then the operatorList was never - // requested before. Make the request and create the promise. - if (!this.displayReadyPromise) { - this.pdf.startRendering(this); - this.displayReadyPromise = new Promise(); - } - - // Once the operatorList and fonts are loaded, do the actual rendering. - this.displayReadyPromise.then( - function pageDisplayReadyPromise() { - var gfx = new CanvasGraphics(ctx, this.objs, textLayer); - try { - this.display(gfx, callback); - } catch (e) { - if (callback) - callback(e); - else - error(e); - } - }.bind(this), - function pageDisplayReadPromiseError(reason) { - if (callback) - callback(reason); - else - error(reason); - } - ); } }; @@ -484,26 +367,26 @@ var Page = (function PageClosure() { })(); /** - * The `PDFDocModel` holds all the data of the PDF file. Compared to the + * The `PDFDocument` holds all the data of the PDF file. Compared to the * `PDFDoc`, this one doesn't have any job management code. - * Right now there exists one PDFDocModel on the main thread + one object + * Right now there exists one PDFDocument on the main thread + one object * for each worker. If there is no worker support enabled, there are two - * `PDFDocModel` objects on the main thread created. + * `PDFDocument` objects on the main thread created. */ -var PDFDocModel = (function PDFDocModelClosure() { - function PDFDocModel(arg, callback) { +var PDFDocument = (function PDFDocumentClosure() { + function PDFDocument(arg, password) { if (isStream(arg)) - init.call(this, arg); + init.call(this, arg, password); else if (isArrayBuffer(arg)) - init.call(this, new Stream(arg)); + init.call(this, new Stream(arg), password); else - error('PDFDocModel: Unknown argument type'); + error('PDFDocument: Unknown argument type'); } - function init(stream) { + function init(stream, password) { assertWellFormed(stream.length > 0, 'stream must have data'); this.stream = stream; - this.setup(); + this.setup(password); this.acroForm = this.catalog.catDict.get('AcroForm'); } @@ -523,7 +406,7 @@ var PDFDocModel = (function PDFDocModelClosure() { return true; /* found */ } - PDFDocModel.prototype = { + PDFDocument.prototype = { get linearization() { var length = this.stream.length; var linearization = false; @@ -584,7 +467,7 @@ var PDFDocModel = (function PDFDocModelClosure() { }, // Find the header, remove leading garbage and setup the stream // starting from the header. - checkHeader: function PDFDocModel_checkHeader() { + checkHeader: function PDFDocument_checkHeader() { var stream = this.stream; stream.reset(); if (find(stream, '%PDF-', 1024)) { @@ -594,11 +477,12 @@ var PDFDocModel = (function PDFDocModelClosure() { } // May not be a PDF file, continue anyway. }, - setup: function PDFDocModel_setup(ownerPassword, userPassword) { + setup: function PDFDocument_setup(password) { this.checkHeader(); var xref = new XRef(this.stream, this.startXRef, - this.mainXRefEntriesOffset); + this.mainXRefEntriesOffset, + password); this.xref = xref; this.catalog = new Catalog(xref); }, @@ -608,7 +492,7 @@ var PDFDocModel = (function PDFDocModelClosure() { // shadow the prototype getter return shadow(this, 'numPages', num); }, - getDocumentInfo: function PDFDocModel_getDocumentInfo() { + getDocumentInfo: function PDFDocument_getDocumentInfo() { var info; if (this.xref.trailer.has('Info')) { var infoDict = this.xref.trailer.get('Info'); @@ -622,7 +506,7 @@ var PDFDocModel = (function PDFDocModelClosure() { return shadow(this, 'getDocumentInfo', info); }, - getFingerprint: function PDFDocModel_getFingerprint() { + getFingerprint: function PDFDocument_getFingerprint() { var xref = this.xref, fileID; if (xref.trailer.has('ID')) { fileID = ''; @@ -643,259 +527,22 @@ var PDFDocModel = (function PDFDocModelClosure() { return shadow(this, 'getFingerprint', fileID); }, - getPage: function PDFDocModel_getPage(n) { + getPage: function PDFDocument_getPage(n) { return this.catalog.getPage(n); } }; - return PDFDocModel; + return PDFDocument; })(); -var PDFDoc = (function PDFDocClosure() { - function PDFDoc(arg, callback) { - var stream = null; - var data = null; - - if (isStream(arg)) { - stream = arg; - data = arg.bytes; - } else if (isArrayBuffer(arg)) { - stream = new Stream(arg); - data = arg; - } else { - error('PDFDoc: Unknown argument type'); - } - - this.data = data; - this.stream = stream; - this.pdfModel = new PDFDocModel(stream); - this.fingerprint = this.pdfModel.getFingerprint(); - this.info = this.pdfModel.getDocumentInfo(); - this.catalog = this.pdfModel.catalog; - this.objs = new PDFObjects(); - - this.pageCache = []; - this.fontsLoading = {}; - this.workerReadyPromise = new Promise('workerReady'); - - // If worker support isn't disabled explicit and the browser has worker - // support, create a new web worker and test if it/the browser fullfills - // all requirements to run parts of pdf.js in a web worker. - // Right now, the requirement is, that an Uint8Array is still an Uint8Array - // as it arrives on the worker. Chrome added this with version 15. - if (!globalScope.PDFJS.disableWorker && typeof Worker !== 'undefined') { - var workerSrc = PDFJS.workerSrc; - if (typeof workerSrc === 'undefined') { - error('No PDFJS.workerSrc specified'); - } - - try { - var worker; - if (PDFJS.isFirefoxExtension) { - // The firefox extension can't load the worker from the resource:// - // url so we have to inline the script and then use the blob loader. - var bb = new MozBlobBuilder(); - bb.append(document.querySelector('#PDFJS_SCRIPT_TAG').textContent); - var blobUrl = window.URL.createObjectURL(bb.getBlob()); - worker = new Worker(blobUrl); - } else { - // Some versions of FF can't create a worker on localhost, see: - // https://bugzilla.mozilla.org/show_bug.cgi?id=683280 - worker = new Worker(workerSrc); - } - - var messageHandler = new MessageHandler('main', worker); - - messageHandler.on('test', function pdfDocTest(supportTypedArray) { - if (supportTypedArray) { - this.worker = worker; - this.setupMessageHandler(messageHandler); - } else { - globalScope.PDFJS.disableWorker = true; - this.setupFakeWorker(); - } - }.bind(this)); - - var testObj = new Uint8Array(1); - // Some versions of Opera throw a DATA_CLONE_ERR on - // serializing the typed array. - messageHandler.send('test', testObj); - return; - } catch (e) { - warn('The worker has been disabled.'); - } - } - // Either workers are disabled, not supported or have thrown an exception. - // Thus, we fallback to a faked worker. - globalScope.PDFJS.disableWorker = true; - this.setupFakeWorker(); - } - - PDFDoc.prototype = { - setupFakeWorker: function PDFDoc_setupFakeWorker() { - // If we don't use a worker, just post/sendMessage to the main thread. - var fakeWorker = { - postMessage: function PDFDoc_postMessage(obj) { - fakeWorker.onmessage({data: obj}); - }, - terminate: function PDFDoc_terminate() {} - }; - - var messageHandler = new MessageHandler('main', fakeWorker); - this.setupMessageHandler(messageHandler); - - // If the main thread is our worker, setup the handling for the messages - // the main thread sends to it self. - WorkerMessageHandler.setup(messageHandler); - }, - - - setupMessageHandler: function PDFDoc_setupMessageHandler(messageHandler) { - this.messageHandler = messageHandler; - - messageHandler.on('page', function pdfDocPage(data) { - var pageNum = data.pageNum; - var page = this.pageCache[pageNum]; - var depFonts = data.depFonts; - - page.stats.timeEnd('Page Request'); - page.startRenderingFromOperatorList(data.operatorList, depFonts); - }, this); - - messageHandler.on('obj', function pdfDocObj(data) { - var id = data[0]; - var type = data[1]; - - switch (type) { - case 'JpegStream': - var imageData = data[2]; - loadJpegStream(id, imageData, this.objs); - break; - case 'Image': - var imageData = data[2]; - this.objs.resolve(id, imageData); - break; - case 'Font': - var name = data[2]; - var file = data[3]; - var properties = data[4]; - - if (file) { - // Rewrap the ArrayBuffer in a stream. - var fontFileDict = new Dict(); - file = new Stream(file, 0, file.length, fontFileDict); - } - - // At this point, only the font object is created but the font is - // not yet attached to the DOM. This is done in `FontLoader.bind`. - var font = new Font(name, file, properties); - this.objs.resolve(id, font); - break; - default: - error('Got unkown object type ' + type); - } - }, this); - - messageHandler.on('page_error', function pdfDocError(data) { - var page = this.pageCache[data.pageNum]; - if (page.displayReadyPromise) - page.displayReadyPromise.reject(data.error); - else - error(data.error); - }, this); - - messageHandler.on('jpeg_decode', function(data, promise) { - var imageData = data[0]; - var components = data[1]; - if (components != 3 && components != 1) - error('Only 3 component or 1 component can be returned'); - - var img = new Image(); - img.onload = (function messageHandler_onloadClosure() { - var width = img.width; - var height = img.height; - var size = width * height; - var rgbaLength = size * 4; - var buf = new Uint8Array(size * components); - var tmpCanvas = createScratchCanvas(width, height); - var tmpCtx = tmpCanvas.getContext('2d'); - tmpCtx.drawImage(img, 0, 0); - var data = tmpCtx.getImageData(0, 0, width, height).data; - - if (components == 3) { - for (var i = 0, j = 0; i < rgbaLength; i += 4, j += 3) { - buf[j] = data[i]; - buf[j + 1] = data[i + 1]; - buf[j + 2] = data[i + 2]; - } - } else if (components == 1) { - for (var i = 0, j = 0; i < rgbaLength; i += 4, j++) { - buf[j] = data[i]; - } - } - promise.resolve({ data: buf, width: width, height: height}); - }).bind(this); - var src = 'data:image/jpeg;base64,' + window.btoa(imageData); - img.src = src; - }); - - setTimeout(function pdfDocFontReadySetTimeout() { - messageHandler.send('doc', this.data); - this.workerReadyPromise.resolve(true); - }.bind(this)); - }, - - get numPages() { - return this.pdfModel.numPages; - }, - - startRendering: function PDFDoc_startRendering(page) { - // The worker might not be ready to receive the page request yet. - this.workerReadyPromise.then(function pdfDocStartRenderingThen() { - page.stats.time('Page Request'); - this.messageHandler.send('page_request', page.pageNumber + 1); - }.bind(this)); - }, - - getPage: function PDFDoc_getPage(n) { - if (this.pageCache[n]) - return this.pageCache[n]; - - var page = this.pdfModel.getPage(n); - // Add a reference to the objects such that Page can forward the reference - // to the CanvasGraphics and so on. - page.objs = this.objs; - page.pdf = this; - return (this.pageCache[n] = page); - }, - - destroy: function PDFDoc_destroy() { - if (this.worker) - this.worker.terminate(); - - if (this.fontWorker) - this.fontWorker.terminate(); - - for (var n in this.pageCache) - delete this.pageCache[n]; - - delete this.data; - delete this.stream; - delete this.pdf; - delete this.catalog; - } - }; - - return PDFDoc; -})(); - -globalScope.PDFJS.PDFDoc = PDFDoc; /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ 'use strict'; +// Use only for debugging purposes. This should not be used in any code that is +// in mozilla master. function log(msg) { if (console && console.log) console.log(msg); @@ -903,9 +550,36 @@ function log(msg) { print(msg); } +// A notice for devs that will not trigger the fallback UI. These are good +// for things that are helpful to devs, such as warning that Workers were +// disabled, which is important to devs but not end users. +function info(msg) { + if (verbosity >= INFOS) { + log('Info: ' + msg); + PDFJS.LogManager.notify('info', msg); + } +} + +// Non-fatal warnings that should trigger the fallback UI. function warn(msg) { - if (verbosity >= WARNINGS) + if (verbosity >= WARNINGS) { log('Warning: ' + msg); + PDFJS.LogManager.notify('warn', msg); + } +} + +// Fatal errors that should trigger the fallback UI and halt execution by +// throwing an exception. +function error(msg) { + log('Error: ' + msg); + log(backtrace()); + PDFJS.LogManager.notify('error', msg); + throw new Error(msg); +} + +// Missing features that should trigger the fallback UI. +function TODO(what) { + warn('TODO: ' + what); } function backtrace() { @@ -916,21 +590,6 @@ function backtrace() { } } -function error(msg) { - log('Error: ' + msg); - log(backtrace()); - throw new Error(msg); -} - -function TODO(what) { - if (verbosity >= TODOS) - log('TODO: ' + what); -} - -function malformed(msg) { - error('Malformed PDF: ' + msg); -} - function assert(cond, msg) { if (!cond) error(msg); @@ -940,9 +599,25 @@ function assert(cond, msg) { // behavior is undefined. function assertWellFormed(cond, msg) { if (!cond) - malformed(msg); + error(msg); } +var LogManager = PDFJS.LogManager = (function LogManagerClosure() { + var loggers = []; + return { + addLogger: function logManager_addLogger(logger) { + loggers.push(logger); + }, + notify: function(type, message) { + for (var i = 0, ii = loggers.length; i < ii; i++) { + var logger = loggers[i]; + if (logger[type]) + logger[type](message); + } + } + }; +})(); + function shadow(obj, prop, value) { Object.defineProperty(obj, prop, { value: value, enumerable: true, @@ -951,6 +626,19 @@ function shadow(obj, prop, value) { return value; } +var PasswordException = (function PasswordExceptionClosure() { + function PasswordException(msg, code) { + this.name = 'PasswordException'; + this.message = msg; + this.code = code; + } + + PasswordException.prototype = new Error(); + PasswordException.constructor = PasswordException; + + return PasswordException; +})(); + function bytesToString(bytes) { var str = ''; var length = bytes.length; @@ -969,7 +657,7 @@ function stringToBytes(str) { var IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0]; -var Util = (function UtilClosure() { +var Util = PDFJS.Util = (function UtilClosure() { function Util() {} Util.makeCssRgb = function Util_makeCssRgb(r, g, b) { @@ -990,6 +678,19 @@ var Util = (function UtilClosure() { return [xt, yt]; }; + Util.applyInverseTransform = function Util_applyInverseTransform(p, m) { + var d = m[0] * m[3] - m[1] * m[2]; + var xt = (p[0] * m[3] - p[1] * m[2] + m[2] * m[5] - m[4] * m[3]) / d; + var yt = (-p[0] * m[1] + p[1] * m[0] + m[4] * m[1] - m[5] * m[0]) / d; + return [xt, yt]; + }; + + Util.inverseTransform = function Util_inverseTransform(m) { + var d = m[0] * m[3] - m[1] * m[2]; + return [m[3] / d, -m[1] / d, -m[2] / d, m[0] / d, + (m[2] * m[5] - m[4] * m[3]) / d, (m[4] * m[1] - m[5] * m[0]) / d]; + }; + // Apply a generic 3d matrix M on a 3-vector v: // | a b c | | X | // | d e f | x | Y | @@ -1058,7 +759,7 @@ var Util = (function UtilClosure() { } return result; - } + }; Util.sign = function Util_sign(num) { return num < 0 ? -1 : 1; @@ -1067,6 +768,80 @@ var Util = (function UtilClosure() { return Util; })(); +var PageViewport = PDFJS.PageViewport = (function PageViewportClosure() { + function PageViewport(viewBox, scale, rotate, offsetX, offsetY) { + // creating transform to convert pdf coordinate system to the normal + // canvas like coordinates taking in account scale and rotation + var centerX = (viewBox[2] + viewBox[0]) / 2; + var centerY = (viewBox[3] + viewBox[1]) / 2; + var rotateA, rotateB, rotateC, rotateD; + switch (rotate) { + case -180: + case 180: + rotateA = -1; rotateB = 0; rotateC = 0; rotateD = 1; + break; + case -270: + case 90: + rotateA = 0; rotateB = 1; rotateC = 1; rotateD = 0; + break; + case -90: + case 270: + rotateA = 0; rotateB = -1; rotateC = -1; rotateD = 0; + break; + case 360: + case 0: + default: + rotateA = 1; rotateB = 0; rotateC = 0; rotateD = -1; + break; + } + var offsetCanvasX, offsetCanvasY; + var width, height; + if (rotateA == 0) { + offsetCanvasX = Math.abs(centerY - viewBox[1]) * scale + offsetX; + offsetCanvasY = Math.abs(centerX - viewBox[0]) * scale + offsetY; + width = Math.abs(viewBox[3] - viewBox[1]) * scale; + height = Math.abs(viewBox[2] - viewBox[0]) * scale; + } else { + offsetCanvasX = Math.abs(centerX - viewBox[0]) * scale + offsetX; + offsetCanvasY = Math.abs(centerY - viewBox[1]) * scale + offsetY; + width = Math.abs(viewBox[2] - viewBox[0]) * scale; + height = Math.abs(viewBox[3] - viewBox[1]) * scale; + } + // creating transform for the following operations: + // translate(-centerX, -centerY), rotate and flip vertically, + // scale, and translate(offsetCanvasX, offsetCanvasY) + this.transform = [ + rotateA * scale, + rotateB * scale, + rotateC * scale, + rotateD * scale, + offsetCanvasX - rotateA * scale * centerX - rotateC * scale * centerY, + offsetCanvasY - rotateB * scale * centerX - rotateD * scale * centerY + ]; + + this.offsetX = offsetX; + this.offsetY = offsetY; + this.width = width; + this.height = height; + this.fontScale = scale; + } + PageViewport.prototype = { + convertToViewportPoint: function PageViewport_convertToViewportPoint(x, y) { + return Util.applyTransform([x, y], this.transform); + }, + convertToViewportRectangle: + function PageViewport_convertToViewportRectangle(rect) { + var tl = Util.applyTransform([rect[0], rect[1]], this.transform); + var br = Util.applyTransform([rect[2], rect[3]], this.transform); + return [tl[0], tl[1], br[0], br[1]]; + }, + convertToPdfPoint: function PageViewport_convertToPdfPoint(x, y) { + return Util.applyInverseTransform([x, y], this.transform); + } + }; + return PageViewport; +})(); + var PDFStringTranslateTable = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2D8, 0x2C7, 0x2C6, 0x2D9, 0x2DD, 0x2DB, 0x2DA, 0x2DC, 0, 0, 0, 0, 0, 0, 0, @@ -1095,6 +870,10 @@ function stringToPDFString(str) { return str2; } +function stringToUTF8String(str) { + return decodeURIComponent(escape(str)); +} + function isBool(v) { return typeof v == 'boolean'; } @@ -1168,7 +947,7 @@ function isPDFFunction(v) { * can be set. If any of these happens twice or the data is required before * it was set, an exception is throw. */ -var Promise = (function PromiseClosure() { +var Promise = PDFJS.Promise = (function PromiseClosure() { var EMPTY_PROMISE = {}; /** @@ -1190,6 +969,7 @@ var Promise = (function PromiseClosure() { } this.callbacks = []; this.errbacks = []; + this.progressbacks = []; }; /** * Builds a promise that is resolved when all the passed in promises are @@ -1205,7 +985,7 @@ var Promise = (function PromiseClosure() { deferred.resolve(results); return deferred; } - for (var i = 0; i < unresolved; ++i) { + for (var i = 0, ii = promises.length; i < ii; ++i) { var promise = promises[i]; promise.then((function(i) { return function(value) { @@ -1261,7 +1041,7 @@ var Promise = (function PromiseClosure() { } this.isResolved = true; - this.data = data || null; + this.data = (typeof data !== 'undefined') ? data : null; var callbacks = this.callbacks; for (var i = 0, ii = callbacks.length; i < ii; i++) { @@ -1269,7 +1049,14 @@ var Promise = (function PromiseClosure() { } }, - reject: function Promise_reject(reason) { + progress: function Promise_progress(data) { + var callbacks = this.progressbacks; + for (var i = 0, ii = callbacks.length; i < ii; i++) { + callbacks[i].call(null, data); + } + }, + + reject: function Promise_reject(reason, exception) { if (this.isRejected) { error('A Promise can be rejected only once ' + this.name); } @@ -1282,11 +1069,11 @@ var Promise = (function PromiseClosure() { var errbacks = this.errbacks; for (var i = 0, ii = errbacks.length; i < ii; i++) { - errbacks[i].call(null, reason); + errbacks[i].call(null, reason, exception); } }, - then: function Promise_then(callback, errback) { + then: function Promise_then(callback, errback, progressback) { if (!callback) { error('Requiring callback' + this.name); } @@ -1303,6 +1090,9 @@ var Promise = (function PromiseClosure() { if (errback) this.errbacks.push(errback); } + + if (progressback) + this.progressbacks.push(progressback); } }; @@ -1361,6 +1151,659 @@ var StatTimer = (function StatTimerClosure() { }; return StatTimer; })(); + +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ + +/** + * This is the main entry point for loading a PDF and interacting with it. + * NOTE: If a URL is used to fetch the PDF data a standard XMLHttpRequest(XHR) + * is used, which means it must follow the same origin rules that any XHR does + * e.g. No cross domain requests without CORS. + * + * @param {string|TypedAray|object} source Can be an url to where a PDF is + * located, a typed array (Uint8Array) already populated with data or + * and parameter object with the following possible fields: + * - url - The URL of the PDF. + * - data - A typed array with PDF data. + * - httpHeaders - Basic authentication headers. + * - password - For decrypting password-protected PDFs. + * + * @return {Promise} A promise that is resolved with {PDFDocumentProxy} object. + */ +PDFJS.getDocument = function getDocument(source) { + var url, data, headers, password, parameters = {}; + if (typeof source === 'string') { + url = source; + } else if (isArrayBuffer(source)) { + data = source; + } else if (typeof source === 'object') { + url = source.url; + data = source.data; + headers = source.httpHeaders; + password = source.password; + parameters.password = password || null; + + if (!url && !data) + error('Invalid parameter array, need either .data or .url'); + } else { + error('Invalid parameter in getDocument, need either Uint8Array, ' + + 'string or a parameter object'); + } + + var promise = new PDFJS.Promise(); + var transport = new WorkerTransport(promise); + if (data) { + // assuming the data is array, instantiating directly from it + transport.sendData(data, parameters); + } else if (url) { + // fetch url + PDFJS.getPdf( + { + url: url, + progress: function getPDFProgress(evt) { + if (evt.lengthComputable) + promise.progress({ + loaded: evt.loaded, + total: evt.total + }); + }, + error: function getPDFError(e) { + promise.reject('Unexpected server response of ' + + e.target.status + '.'); + }, + headers: headers + }, + function getPDFLoad(data) { + transport.sendData(data, parameters); + }); + } + + return promise; +}; + +/** + * Proxy to a PDFDocument in the worker thread. Also, contains commonly used + * properties that can be read synchronously. + */ +var PDFDocumentProxy = (function PDFDocumentProxyClosure() { + function PDFDocumentProxy(pdfInfo, transport) { + this.pdfInfo = pdfInfo; + this.transport = transport; + } + PDFDocumentProxy.prototype = { + /** + * @return {number} Total number of pages the PDF contains. + */ + get numPages() { + return this.pdfInfo.numPages; + }, + /** + * @return {string} A unique ID to identify a PDF. Not guaranteed to be + * unique. + */ + get fingerprint() { + return this.pdfInfo.fingerprint; + }, + /** + * @param {number} The page number to get. The first page is 1. + * @return {Promise} A promise that is resolved with a {PDFPageProxy} + * object. + */ + getPage: function PDFDocumentProxy_getPage(number) { + return this.transport.getPage(number); + }, + /** + * @return {Promise} A promise that is resolved with a lookup table for + * mapping named destinations to reference numbers. + */ + getDestinations: function PDFDocumentProxy_getDestinations() { + var promise = new PDFJS.Promise(); + var destinations = this.pdfInfo.destinations; + promise.resolve(destinations); + return promise; + }, + /** + * @return {Promise} A promise that is resolved with an {array} that is a + * tree outline (if it has one) of the PDF. The tree is in the format of: + * [ + * { + * title: string, + * bold: boolean, + * italic: boolean, + * color: rgb array, + * dest: dest obj, + * items: array of more items like this + * }, + * ... + * ]. + */ + getOutline: function PDFDocumentProxy_getOutline() { + var promise = new PDFJS.Promise(); + var outline = this.pdfInfo.outline; + promise.resolve(outline); + return promise; + }, + /** + * @return {Promise} A promise that is resolved with an {object} that has + * info and metadata properties. Info is an {object} filled with anything + * available in the information dictionary and similarly metadata is a + * {Metadata} object with information from the metadata section of the PDF. + */ + getMetadata: function PDFDocumentProxy_getMetadata() { + var promise = new PDFJS.Promise(); + var info = this.pdfInfo.info; + var metadata = this.pdfInfo.metadata; + promise.resolve({ + info: info, + metadata: metadata ? new PDFJS.Metadata(metadata) : null + }); + return promise; + }, + isEncrypted: function PDFDocumentProxy_isEncrypted() { + var promise = new PDFJS.Promise(); + promise.resolve(this.pdfInfo.encrypted); + return promise; + }, + /** + * @return {Promise} A promise that is resolved with a TypedArray that has + * the raw data from the PDF. + */ + getData: function PDFDocumentProxy_getData() { + var promise = new PDFJS.Promise(); + this.transport.getData(promise); + return promise; + }, + destroy: function PDFDocumentProxy_destroy() { + this.transport.destroy(); + } + }; + return PDFDocumentProxy; +})(); + +var PDFPageProxy = (function PDFPageProxyClosure() { + function PDFPageProxy(pageInfo, transport) { + this.pageInfo = pageInfo; + this.transport = transport; + this.stats = new StatTimer(); + this.stats.enabled = !!globalScope.PDFJS.enableStats; + this.objs = transport.objs; + this.renderInProgress = false; + } + PDFPageProxy.prototype = { + /** + * @return {number} Page number of the page. First page is 1. + */ + get pageNumber() { + return this.pageInfo.pageIndex + 1; + }, + /** + * @return {number} The number of degrees the page is rotated clockwise. + */ + get rotate() { + return this.pageInfo.rotate; + }, + /** + * @return {object} The reference that points to this page. It has 'num' and + * 'gen' properties. + */ + get ref() { + return this.pageInfo.ref; + }, + /** + * @return {array} An array of the visible portion of the PDF page in the + * user space units - [x1, y1, x2, y2]. + */ + get view() { + return this.pageInfo.view; + }, + /** + * @param {number} scale The desired scale of the viewport. + * @param {number} rotate Degrees to rotate the viewport. If omitted this + * defaults to the page rotation. + * @return {PageViewport} Contains 'width' and 'height' properties along + * with transforms required for rendering. + */ + getViewport: function PDFPageProxy_getViewport(scale, rotate) { + if (arguments.length < 2) + rotate = this.rotate; + return new PDFJS.PageViewport(this.view, scale, rotate, 0, 0); + }, + /** + * @return {Promise} A promise that is resolved with an {array} of the + * annotation objects. + */ + getAnnotations: function PDFPageProxy_getAnnotations() { + if (this.annotationsPromise) + return this.annotationsPromise; + + var promise = new PDFJS.Promise(); + this.annotationsPromise = promise; + this.transport.getAnnotations(this.pageInfo.pageIndex); + return promise; + }, + /** + * Begins the process of rendering a page to the desired context. + * @param {object} params A parameter object that supports: + * { + * canvasContext(required): A 2D context of a DOM Canvas object., + * textLayer(optional): An object that has beginLayout, endLayout, and + * appendText functions. + * }. + * @return {Promise} A promise that is resolved when the page finishes + * rendering. + */ + render: function PDFPageProxy_render(params) { + this.renderInProgress = true; + + var promise = new Promise(); + var stats = this.stats; + stats.time('Overall'); + // If there is no displayReadyPromise yet, then the operatorList was never + // requested before. Make the request and create the promise. + if (!this.displayReadyPromise) { + this.displayReadyPromise = new Promise(); + this.destroyed = false; + + this.stats.time('Page Request'); + this.transport.messageHandler.send('RenderPageRequest', { + pageIndex: this.pageNumber - 1 + }); + } + + var self = this; + function complete(error) { + self.renderInProgress = false; + if (self.destroyed) { + delete self.operatorList; + delete self.displayReadyPromise; + } + + if (error) + promise.reject(error); + else + promise.resolve(); + }; + + // Once the operatorList and fonts are loaded, do the actual rendering. + this.displayReadyPromise.then( + function pageDisplayReadyPromise() { + if (self.destroyed) { + complete(); + return; + } + + var gfx = new CanvasGraphics(params.canvasContext, + this.objs, params.textLayer); + try { + this.display(gfx, params.viewport, complete); + } catch (e) { + complete(e); + } + }.bind(this), + function pageDisplayReadPromiseError(reason) { + complete(reason); + } + ); + + return promise; + }, + /** + * For internal use only. + */ + startRenderingFromOperatorList: + function PDFPageProxy_startRenderingFromOperatorList(operatorList, + fonts) { + var self = this; + this.operatorList = operatorList; + + var displayContinuation = function pageDisplayContinuation() { + // Always defer call to display() to work around bug in + // Firefox error reporting from XHR callbacks. + setTimeout(function pageSetTimeout() { + self.displayReadyPromise.resolve(); + }); + }; + + this.ensureFonts(fonts, + function pageStartRenderingFromOperatorListEnsureFonts() { + displayContinuation(); + } + ); + }, + /** + * For internal use only. + */ + ensureFonts: function PDFPageProxy_ensureFonts(fonts, callback) { + this.stats.time('Font Loading'); + // Convert the font names to the corresponding font obj. + for (var i = 0, ii = fonts.length; i < ii; i++) { + fonts[i] = this.objs.objs[fonts[i]].data; + } + + // Load all the fonts + FontLoader.bind( + fonts, + function pageEnsureFontsFontObjs(fontObjs) { + this.stats.timeEnd('Font Loading'); + + callback.call(this); + }.bind(this) + ); + }, + /** + * For internal use only. + */ + display: function PDFPageProxy_display(gfx, viewport, callback) { + var stats = this.stats; + stats.time('Rendering'); + + gfx.beginDrawing(viewport); + + var startIdx = 0; + var length = this.operatorList.fnArray.length; + var operatorList = this.operatorList; + var stepper = null; + if (PDFJS.pdfBug && StepperManager.enabled) { + stepper = StepperManager.create(this.pageNumber - 1); + stepper.init(operatorList); + stepper.nextBreakPoint = stepper.getNextBreakPoint(); + } + + var self = this; + function next() { + startIdx = + gfx.executeOperatorList(operatorList, startIdx, next, stepper); + if (startIdx == length) { + gfx.endDrawing(); + stats.timeEnd('Rendering'); + stats.timeEnd('Overall'); + if (callback) callback(); + } + } + next(); + }, + /** + * @return {Promise} That is resolved with the a {string} that is the text + * content from the page. + */ + getTextContent: function PDFPageProxy_getTextContent() { + var promise = new PDFJS.Promise(); + this.transport.messageHandler.send('GetTextContent', { + pageIndex: this.pageNumber - 1 + }, + function textContentCallback(textContent) { + promise.resolve(textContent); + } + ); + return promise; + }, + /** + * Stub for future feature. + */ + getOperationList: function PDFPageProxy_getOperationList() { + var promise = new PDFJS.Promise(); + var operationList = { // not implemented + dependencyFontsID: null, + operatorList: null + }; + promise.resolve(operationList); + return promise; + }, + /** + * Destroys resources allocated by the page. + */ + destroy: function PDFPageProxy_destroy() { + this.destroyed = true; + + if (!this.renderInProgress) { + delete this.operatorList; + delete this.displayReadyPromise; + } + } + }; + return PDFPageProxy; +})(); +/** + * For internal use only. + */ +var WorkerTransport = (function WorkerTransportClosure() { + function WorkerTransport(promise) { + this.workerReadyPromise = promise; + this.objs = new PDFObjects(); + + this.pageCache = []; + this.pagePromises = []; + this.fontsLoading = {}; + + // If worker support isn't disabled explicit and the browser has worker + // support, create a new web worker and test if it/the browser fullfills + // all requirements to run parts of pdf.js in a web worker. + // Right now, the requirement is, that an Uint8Array is still an Uint8Array + // as it arrives on the worker. Chrome added this with version 15. + if (!globalScope.PDFJS.disableWorker && typeof Worker !== 'undefined') { + var workerSrc = PDFJS.workerSrc; + if (typeof workerSrc === 'undefined') { + error('No PDFJS.workerSrc specified'); + } + + try { + var worker; + if (PDFJS.isFirefoxExtension) { + // The firefox extension can't load the worker from the resource:// + // url so we have to inline the script and then use the blob loader. + var bb = new MozBlobBuilder(); + bb.append(document.querySelector('#PDFJS_SCRIPT_TAG').textContent); + var blobUrl = window.URL.createObjectURL(bb.getBlob()); + worker = new Worker(blobUrl); + } else { + // Some versions of FF can't create a worker on localhost, see: + // https://bugzilla.mozilla.org/show_bug.cgi?id=683280 + worker = new Worker(workerSrc); + } + + var messageHandler = new MessageHandler('main', worker); + this.messageHandler = messageHandler; + + messageHandler.on('test', function transportTest(supportTypedArray) { + if (supportTypedArray) { + this.worker = worker; + this.setupMessageHandler(messageHandler); + } else { + globalScope.PDFJS.disableWorker = true; + this.setupFakeWorker(); + } + }.bind(this)); + + var testObj = new Uint8Array(1); + // Some versions of Opera throw a DATA_CLONE_ERR on + // serializing the typed array. + messageHandler.send('test', testObj); + return; + } catch (e) { + info('The worker has been disabled.'); + } + } + // Either workers are disabled, not supported or have thrown an exception. + // Thus, we fallback to a faked worker. + globalScope.PDFJS.disableWorker = true; + this.setupFakeWorker(); + } + WorkerTransport.prototype = { + destroy: function WorkerTransport_destroy() { + if (this.worker) + this.worker.terminate(); + + this.pageCache = []; + this.pagePromises = []; + }, + setupFakeWorker: function WorkerTransport_setupFakeWorker() { + // If we don't use a worker, just post/sendMessage to the main thread. + var fakeWorker = { + postMessage: function WorkerTransport_postMessage(obj) { + fakeWorker.onmessage({data: obj}); + }, + terminate: function WorkerTransport_terminate() {} + }; + + var messageHandler = new MessageHandler('main', fakeWorker); + this.setupMessageHandler(messageHandler); + + // If the main thread is our worker, setup the handling for the messages + // the main thread sends to it self. + WorkerMessageHandler.setup(messageHandler); + }, + + setupMessageHandler: + function WorkerTransport_setupMessageHandler(messageHandler) { + this.messageHandler = messageHandler; + + messageHandler.on('GetDoc', function transportDoc(data) { + var pdfInfo = data.pdfInfo; + var pdfDocument = new PDFDocumentProxy(pdfInfo, this); + this.pdfDocument = pdfDocument; + this.workerReadyPromise.resolve(pdfDocument); + }, this); + + messageHandler.on('NeedPassword', function transportPassword(data) { + this.workerReadyPromise.reject(data.exception.message, data.exception); + }, this); + + messageHandler.on('IncorrectPassword', function transportBadPass(data) { + this.workerReadyPromise.reject(data.exception.message, data.exception); + }, this); + + messageHandler.on('GetPage', function transportPage(data) { + var pageInfo = data.pageInfo; + var page = new PDFPageProxy(pageInfo, this); + this.pageCache[pageInfo.pageIndex] = page; + var promise = this.pagePromises[pageInfo.pageIndex]; + promise.resolve(page); + }, this); + + messageHandler.on('GetAnnotations', function transportAnnotations(data) { + var annotations = data.annotations; + var promise = this.pageCache[data.pageIndex].annotationsPromise; + promise.resolve(annotations); + }, this); + + messageHandler.on('RenderPage', function transportRender(data) { + var page = this.pageCache[data.pageIndex]; + var depFonts = data.depFonts; + + page.stats.timeEnd('Page Request'); + page.startRenderingFromOperatorList(data.operatorList, depFonts); + }, this); + + messageHandler.on('obj', function transportObj(data) { + var id = data[0]; + var type = data[1]; + if (this.objs.hasData(id)) + return; + + switch (type) { + case 'JpegStream': + var imageData = data[2]; + loadJpegStream(id, imageData, this.objs); + break; + case 'Image': + var imageData = data[2]; + this.objs.resolve(id, imageData); + break; + case 'Font': + var name = data[2]; + var file = data[3]; + var properties = data[4]; + + if (file) { + // Rewrap the ArrayBuffer in a stream. + var fontFileDict = new Dict(); + file = new Stream(file, 0, file.length, fontFileDict); + } + + // At this point, only the font object is created but the font is + // not yet attached to the DOM. This is done in `FontLoader.bind`. + var font = new Font(name, file, properties); + this.objs.resolve(id, font); + break; + default: + error('Got unkown object type ' + type); + } + }, this); + + messageHandler.on('PageError', function transportError(data) { + var page = this.pageCache[data.pageNum - 1]; + if (page.displayReadyPromise) + page.displayReadyPromise.reject(data.error); + else + error(data.error); + }, this); + + messageHandler.on('JpegDecode', function(data, promise) { + var imageData = data[0]; + var components = data[1]; + if (components != 3 && components != 1) + error('Only 3 component or 1 component can be returned'); + + var img = new Image(); + img.onload = (function messageHandler_onloadClosure() { + var width = img.width; + var height = img.height; + var size = width * height; + var rgbaLength = size * 4; + var buf = new Uint8Array(size * components); + var tmpCanvas = createScratchCanvas(width, height); + var tmpCtx = tmpCanvas.getContext('2d'); + tmpCtx.drawImage(img, 0, 0); + var data = tmpCtx.getImageData(0, 0, width, height).data; + + if (components == 3) { + for (var i = 0, j = 0; i < rgbaLength; i += 4, j += 3) { + buf[j] = data[i]; + buf[j + 1] = data[i + 1]; + buf[j + 2] = data[i + 2]; + } + } else if (components == 1) { + for (var i = 0, j = 0; i < rgbaLength; i += 4, j++) { + buf[j] = data[i]; + } + } + promise.resolve({ data: buf, width: width, height: height}); + }).bind(this); + var src = 'data:image/jpeg;base64,' + window.btoa(imageData); + img.src = src; + }); + }, + + sendData: function WorkerTransport_sendData(data, params) { + this.messageHandler.send('GetDocRequest', {data: data, params: params}); + }, + + getData: function WorkerTransport_sendData(promise) { + this.messageHandler.send('GetData', null, function(data) { + promise.resolve(data); + }); + }, + + getPage: function WorkerTransport_getPage(pageNumber, promise) { + var pageIndex = pageNumber - 1; + if (pageIndex in this.pagePromises) + return this.pagePromises[pageIndex]; + var promise = new PDFJS.Promise('Page ' + pageNumber); + this.pagePromises[pageIndex] = promise; + this.messageHandler.send('GetPageRequest', { pageIndex: pageIndex }); + return promise; + }, + + getAnnotations: function WorkerTransport_getAnnotations(pageIndex) { + this.messageHandler.send('GetAnnotationsRequest', + { pageIndex: pageIndex }); + } + }; + return WorkerTransport; + +})(); + /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ @@ -1604,27 +2047,10 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { 'shadingFill': true }, - beginDrawing: function CanvasGraphics_beginDrawing(mediaBox) { - var cw = this.ctx.canvas.width, ch = this.ctx.canvas.height; + beginDrawing: function CanvasGraphics_beginDrawing(viewport) { + var transform = viewport.transform; this.ctx.save(); - switch (mediaBox.rotate) { - case 0: - this.ctx.transform(1, 0, 0, -1, 0, ch); - break; - case 90: - this.ctx.transform(0, 1, 1, 0, 0, 0); - break; - case 180: - this.ctx.transform(-1, 0, 0, 1, cw, 0); - break; - case 270: - this.ctx.transform(0, -1, -1, 0, cw, ch); - break; - } - // Scale so that canvas units are the same as PDF user space units - this.ctx.scale(cw / mediaBox.width, ch / mediaBox.height); - // Move the media left-top corner to the (0,0) canvas position - this.ctx.translate(-mediaBox.x, -mediaBox.y); + this.ctx.transform.apply(this.ctx, transform); if (this.textLayer) this.textLayer.beginLayout(); @@ -1723,10 +2149,13 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { this.ctx.webkitLineDashOffset = dashPhase; }, setRenderingIntent: function CanvasGraphics_setRenderingIntent(intent) { - TODO('set rendering intent: ' + intent); + // Maybe if we one day fully support color spaces this will be important + // for now we can ignore. + // TODO set rendering intent? }, setFlatness: function CanvasGraphics_setFlatness(flatness) { - TODO('set flatness: ' + flatness); + // There's no way to control this with canvas, but we can safely ignore. + // TODO set flatness? }, setGState: function CanvasGraphics_setGState(states) { for (var i = 0, ii = states.length; i < ii; i++) { @@ -2221,7 +2650,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { text.length += shownText.length; } } else { - malformed('TJ array element ' + e + ' is not string or num'); + error('TJ array element ' + e + ' is not string or num'); } } @@ -2474,6 +2903,40 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { } } } + function rescaleImage(pixels, widthScale, heightScale) { + var scaledWidth = Math.ceil(width / widthScale); + var scaledHeight = Math.ceil(height / heightScale); + + var itemsSum = new Uint32Array(scaledWidth * scaledHeight * 4); + var itemsCount = new Uint32Array(scaledWidth * scaledHeight); + for (var i = 0, position = 0; i < height; i++) { + var lineOffset = (0 | (i / heightScale)) * scaledWidth; + for (var j = 0; j < width; j++) { + var countOffset = lineOffset + (0 | (j / widthScale)); + var sumOffset = countOffset << 2; + itemsSum[sumOffset] += pixels[position]; + itemsSum[sumOffset + 1] += pixels[position + 1]; + itemsSum[sumOffset + 2] += pixels[position + 2]; + itemsSum[sumOffset + 3] += pixels[position + 3]; + itemsCount[countOffset]++; + position += 4; + } + } + var tmpCanvas = createScratchCanvas(scaledWidth, scaledHeight); + var tmpCtx = tmpCanvas.getContext('2d'); + var imgData = tmpCtx.getImageData(0, 0, scaledWidth, scaledHeight); + pixels = imgData.data; + for (var i = 0, j = 0, ii = scaledWidth * scaledHeight; i < ii; i++) { + var count = itemsCount[i]; + pixels[j] = itemsSum[j] / count; + pixels[j + 1] = itemsSum[j + 1] / count; + pixels[j + 2] = itemsSum[j + 2] / count; + pixels[j + 3] = itemsSum[j + 3] / count; + j += 4; + } + tmpCtx.putImageData(imgData, 0, 0); + return tmpCanvas; + } this.save(); @@ -2496,8 +2959,19 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { applyStencilMask(pixels, inverseDecode); - tmpCtx.putImageData(imgData, 0, 0); - ctx.drawImage(tmpCanvas, 0, -h); + var currentTransform = ctx.mozCurrentTransformInverse; + var widthScale = Math.max(Math.abs(currentTransform[0]), 1); + var heightScale = Math.max(Math.abs(currentTransform[3]), 1); + if (widthScale >= 2 || heightScale >= 2) { + // canvas does not resize well large images to small -- using simple + // algorithm to perform pre-scaling + tmpCanvas = rescaleImage(imgData.data, widthScale, heightScale); + ctx.scale(widthScale, heightScale); + ctx.drawImage(tmpCanvas, 0, -h / heightScale); + } else { + tmpCtx.putImageData(imgData, 0, 0); + ctx.drawImage(tmpCanvas, 0, -h); + } this.restore(); }, @@ -2624,6 +3098,7 @@ if (!isWorker) { }; } } + /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ @@ -2663,51 +3138,55 @@ var Dict = (function DictClosure() { // xref is optional function Dict(xref) { // Map should only be used internally, use functions below to access. - this.map = Object.create(null); - this.xref = xref; - } + var map = Object.create(null); + + this.assignXref = function Dict_assignXref(newXref) { + xref = newXref; + }; - Dict.prototype = { // automatically dereferences Ref objects - get: function Dict_get(key1, key2, key3) { + this.get = function Dict_get(key1, key2, key3) { var value; - var xref = this.xref; - if (typeof (value = this.map[key1]) != 'undefined' || key1 in this.map || + if (typeof (value = map[key1]) != 'undefined' || key1 in map || typeof key2 == 'undefined') { - return xref ? this.xref.fetchIfRef(value) : value; + return xref ? xref.fetchIfRef(value) : value; } - if (typeof (value = this.map[key2]) != 'undefined' || key2 in this.map || + if (typeof (value = map[key2]) != 'undefined' || key2 in map || typeof key3 == 'undefined') { - return xref ? this.xref.fetchIfRef(value) : value; + return xref ? xref.fetchIfRef(value) : value; } - value = this.map[key3] || null; - return xref ? this.xref.fetchIfRef(value) : value; - }, + value = map[key3] || null; + return xref ? xref.fetchIfRef(value) : value; + }; + // no dereferencing - getRaw: function Dict_getRaw(key) { - return this.map[key]; - }, + this.getRaw = function Dict_getRaw(key) { + return map[key]; + }; + // creates new map and dereferences all Refs - getAll: function Dict_getAll() { + this.getAll = function Dict_getAll() { var all = {}; - for (var key in this.map) - all[key] = this.get(key); + for (var key in map) { + var obj = this.get(key); + all[key] = obj instanceof Dict ? obj.getAll() : obj; + } return all; - }, + }; - set: function Dict_set(key, value) { - this.map[key] = value; - }, + this.set = function Dict_set(key, value) { + map[key] = value; + }; - has: function Dict_has(key) { - return key in this.map; - }, + this.has = function Dict_has(key) { + return key in map; + }; - forEach: function Dict_forEach(callback) { - for (var key in this.map) { + this.forEach = function Dict_forEach(callback) { + for (var key in map) { callback(key, this.get(key)); } - } + }; }; return Dict; @@ -2754,7 +3233,14 @@ var Catalog = (function CatalogClosure() { Catalog.prototype = { get metadata() { - var stream = this.catDict.get('Metadata'); + var streamRef = this.catDict.getRaw('Metadata'); + if (!isRef(streamRef)) + return shadow(this, 'metadata', null); + + var encryptMetadata = !this.xref.encrypt ? false : + this.xref.encrypt.encryptMetadata; + + var stream = this.xref.fetch(streamRef, !encryptMetadata); var metadata; if (stream && isDict(stream.dict)) { var type = stream.dict.get('Type'); @@ -2762,7 +3248,16 @@ var Catalog = (function CatalogClosure() { if (isName(type) && isName(subtype) && type.name === 'Metadata' && subtype.name === 'XML') { - metadata = stringToPDFString(bytesToString(stream.getBytes())); + // XXX: This should examine the charset the XML document defines, + // however since there are currently no real means to decode + // arbitrary charsets, let's just hope that the author of the PDF + // was reasonable enough to stick with the XML default charset, + // which is UTF-8. + try { + metadata = stringToUTF8String(bytesToString(stream.getBytes())); + } catch (e) { + info('Skipping invalid metadata.'); + } } } @@ -2920,12 +3415,12 @@ var Catalog = (function CatalogClosure() { })(); var XRef = (function XRefClosure() { - function XRef(stream, startXRef, mainXRefEntriesOffset) { + function XRef(stream, startXRef, mainXRefEntriesOffset, password) { this.stream = stream; this.entries = []; this.xrefstms = {}; var trailerDict = this.readXRef(startXRef); - trailerDict.xref = this; + trailerDict.assignXref(this); this.trailer = trailerDict; // prepare the XRef cache this.cache = []; @@ -2933,8 +3428,7 @@ var XRef = (function XRefClosure() { var encrypt = trailerDict.get('Encrypt'); if (encrypt) { var fileId = trailerDict.get('ID'); - this.encrypt = new CipherTransformFactory(encrypt, - fileId[0] /*, password */); + this.encrypt = new CipherTransformFactory(encrypt, fileId[0], password); } // get the root dictionary (catalog) object @@ -2986,9 +3480,8 @@ var XRef = (function XRefClosure() { } } - // Sanity check: as per spec, first object must have these properties - if (this.entries[0] && - !(this.entries[0].gen === 65535 && this.entries[0].free)) + // Sanity check: as per spec, first object must be free + if (this.entries[0] && !this.entries[0].free) error('Invalid XRef table: unexpected first object'); // Sanity check @@ -3147,7 +3640,7 @@ var XRef = (function XRefClosure() { } // reading XRef streams for (var i = 0, ii = xrefStms.length; i < ii; ++i) { - this.readXRef(xrefStms[i]); + this.readXRef(xrefStms[i], true); } // finding main trailer var dict; @@ -3170,7 +3663,7 @@ var XRef = (function XRefClosure() { // nothing helps error('Invalid PDF structure'); }, - readXRef: function XRef_readXRef(startXRef) { + readXRef: function XRef_readXRef(startXRef, recoveryMode) { var stream = this.stream; stream.pos = startXRef; @@ -3203,16 +3696,18 @@ var XRef = (function XRefClosure() { error('Invalid XRef stream'); } dict = this.readXRefStream(obj); + if (!dict) + error('Failed to read XRef stream'); } // Recursively get previous dictionary, if any obj = dict.get('Prev'); if (isInt(obj)) - this.readXRef(obj); + this.readXRef(obj, recoveryMode); else if (isRef(obj)) { // The spec says Prev must not be a reference, i.e. "/Prev NNN" // This is a fallback for non-compliant PDFs, i.e. "/Prev NNN 0 R" - this.readXRef(obj.num); + this.readXRef(obj.num, recoveryMode); } return dict; @@ -3220,6 +3715,9 @@ var XRef = (function XRefClosure() { log('(while reading XRef): ' + e); } + if (recoveryMode) + return; + warn('Indexing all PDF objects'); return this.indexObjects(); }, @@ -3432,6 +3930,7 @@ var PDFObjects = (function PDFObjectsClosure() { return PDFObjects; })(); + /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ @@ -4270,7 +4769,10 @@ var PostScriptLexer = (function PostScriptLexerClosure() { // operator var str = ch.toLowerCase(); while (true) { - ch = stream.lookChar().toLowerCase(); + ch = stream.lookChar(); + if (ch === null) + break; + ch = ch.toLowerCase(); if (ch >= 'a' && ch <= 'z') str += ch; else @@ -4306,6 +4808,7 @@ var PostScriptLexer = (function PostScriptLexerClosure() { return PostScriptLexer; })(); + /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ @@ -4411,6 +4914,7 @@ var ExpertSubsetCharset = [ 'periodinferior', 'commainferior' ]; + /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ @@ -11344,6 +11848,7 @@ var CIDToUnicodeMaps = { {f: 7, c: 19887}] }; + /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ @@ -11797,12 +12302,12 @@ var LabCS = (function LabCSClosure() { error('Invalid WhitePoint components, no fallback available'); if (this.XB < 0 || this.YB < 0 || this.ZB < 0) { - warn('Invalid BlackPoint, falling back to default'); + info('Invalid BlackPoint, falling back to default'); this.XB = this.YB = this.ZB = 0; } if (this.amin > this.amax || this.bmin > this.bmax) { - warn('Invalid Range, falling back to defaults'); + info('Invalid Range, falling back to defaults'); this.amin = -100; this.amax = 100; this.bmin = -100; @@ -11876,6 +12381,7 @@ var LabCS = (function LabCSClosure() { }; return LabCS; })(); + /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ @@ -12297,13 +12803,14 @@ var CipherTransform = (function CipherTransformClosure() { })(); var CipherTransformFactory = (function CipherTransformFactoryClosure() { + var defaultPasswordBytes = new Uint8Array([ + 0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41, + 0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08, + 0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80, + 0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A]); + function prepareKeyData(fileId, password, ownerPassword, userPassword, flags, revision, keyLength, encryptMetadata) { - var defaultPasswordBytes = new Uint8Array([ - 0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41, - 0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08, - 0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80, - 0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A]); var hashData = new Uint8Array(100), i = 0, j, n; if (password) { n = Math.min(32, password.length); @@ -12340,9 +12847,8 @@ var CipherTransformFactory = (function CipherTransformFactoryClosure() { var cipher, checkData; if (revision >= 3) { - // padded password in hashData, we can use this array for user - // password check - i = 32; + for (i = 0; i < 32; ++i) + hashData[i] = defaultPasswordBytes[i]; for (j = 0, n = fileId.length; j < n; ++j) hashData[i++] = fileId[j]; cipher = new ARCFourCipher(encryptionKey); @@ -12355,16 +12861,53 @@ var CipherTransformFactory = (function CipherTransformFactoryClosure() { cipher = new ARCFourCipher(derivedKey); checkData = cipher.encryptBlock(checkData); } + for (j = 0, n = checkData.length; j < n; ++j) { + if (userPassword[j] != checkData[j]) + return null; + } } else { cipher = new ARCFourCipher(encryptionKey); - checkData = cipher.encryptBlock(hashData.subarray(0, 32)); - } - for (j = 0, n = checkData.length; j < n; ++j) { - if (userPassword[j] != checkData[j]) - error('incorrect password'); + checkData = cipher.encryptBlock(defaultPasswordBytes); + for (j = 0, n = checkData.length; j < n; ++j) { + if (userPassword[j] != checkData[j]) + return null; + } } return encryptionKey; } + function decodeUserPassword(password, ownerPassword, revision, keyLength) { + var hashData = new Uint8Array(32), i = 0, j, n; + n = Math.min(32, password.length); + for (; i < n; ++i) + hashData[i] = password[i]; + j = 0; + while (i < 32) { + hashData[i++] = defaultPasswordBytes[j++]; + } + var hash = calculateMD5(hashData, 0, i); + var keyLengthInBytes = keyLength >> 3; + if (revision >= 3) { + for (j = 0; j < 50; ++j) { + hash = calculateMD5(hash, 0, hash.length); + } + } + + var cipher, userPassword; + if (revision >= 3) { + userPassword = ownerPassword; + var derivedKey = new Uint8Array(keyLengthInBytes), k; + for (j = 19; j >= 0; j--) { + for (k = 0; k < keyLengthInBytes; ++k) + derivedKey[k] = hash[k] ^ j; + cipher = new ARCFourCipher(derivedKey); + userPassword = cipher.encryptBlock(userPassword); + } + } else { + cipher = new ARCFourCipher(hash.subarray(0, keyLengthInBytes)); + userPassword = cipher.encryptBlock(ownerPassword); + } + return userPassword; + } var identityName = new Name('Identity'); @@ -12387,17 +12930,34 @@ var CipherTransformFactory = (function CipherTransformFactoryClosure() { var userPassword = stringToBytes(dict.get('U')); var flags = dict.get('P'); var revision = dict.get('R'); - var encryptMetadata = + var encryptMetadata = algorithm == 4 && // meaningful when V is 4 dict.get('EncryptMetadata') !== false; // makes true as default value + this.encryptMetadata = encryptMetadata; + var fileIdBytes = stringToBytes(fileId); var passwordBytes; if (password) passwordBytes = stringToBytes(password); - this.encryptionKey = prepareKeyData(fileIdBytes, passwordBytes, - ownerPassword, userPassword, - flags, revision, - keyLength, encryptMetadata); + var encryptionKey = prepareKeyData(fileIdBytes, passwordBytes, + ownerPassword, userPassword, flags, + revision, keyLength, encryptMetadata); + if (!encryptionKey && !password) { + throw new PasswordException('No password given', 'needpassword'); + } else if (!encryptionKey && password) { + // Attempting use the password as an owner password + var decodedPassword = decodeUserPassword(passwordBytes, ownerPassword, + revision, keyLength); + encryptionKey = prepareKeyData(fileIdBytes, decodedPassword, + ownerPassword, userPassword, flags, + revision, keyLength, encryptMetadata); + } + + if (!encryptionKey) + throw new PasswordException('Incorrect Password', 'incorrectpassword'); + + this.encryptionKey = encryptionKey; + if (algorithm == 4) { this.cf = dict.get('CF'); this.stmf = dict.get('StmF') || identityName; @@ -12472,6 +13032,7 @@ var CipherTransformFactory = (function CipherTransformFactoryClosure() { return CipherTransformFactory; })(); + /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ @@ -12582,19 +13143,20 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { // Compatibility BX: 'beginCompat', - EX: 'endCompat' - }; + EX: 'endCompat', - function splitCombinedOperations(operations) { - // Two operations can be combined together, trying to find which two - // operations were concatenated. - for (var i = operations.length - 1; i > 0; i--) { - var op1 = operations.substring(0, i), op2 = operations.substring(i); - if (op1 in OP_MAP && op2 in OP_MAP) - return [op1, op2]; // operations found - } - return null; - } + // (reserved partial commands for the lexer) + BM: null, + BD: null, + 'true': null, + fa: null, + fal: null, + fals: null, + 'false': null, + nu: null, + nul: null, + 'null': null + }; PartialEvaluator.prototype = { getOperatorList: function PartialEvaluator_getOperatorList(stream, @@ -12627,13 +13189,15 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { font = xref.fetchIfRef(font) || fontRes.get(fontName); assertWellFormed(isDict(font)); - if (!font.translated) { + + ++self.objIdCounter; + if (!font.loadedName) { font.translated = self.translateFont(font, xref, resources, dependency); if (font.translated) { // keep track of each font we translated so the caller can // load them asynchronously before calling display on a page - loadedName = 'font_' + uniquePrefix + (++self.objIdCounter); + loadedName = 'font_' + uniquePrefix + self.objIdCounter; font.translated.properties.loadedName = loadedName; font.loadedName = loadedName; @@ -12738,36 +13302,19 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { resources = resources || new Dict(); var xobjs = resources.get('XObject') || new Dict(); var patterns = resources.get('Pattern') || new Dict(); - var parser = new Parser(new Lexer(stream), false, xref); + var parser = new Parser(new Lexer(stream, OP_MAP), false, xref); var res = resources; - var hasNextObj = false, nextObj; var args = [], obj; var TILING_PATTERN = 1, SHADING_PATTERN = 2; while (true) { - if (hasNextObj) { - obj = nextObj; - hasNextObj = false; - } else { - obj = parser.getObj(); - if (isEOF(obj)) - break; - } + obj = parser.getObj(); + if (isEOF(obj)) + break; if (isCmd(obj)) { var cmd = obj.cmd; var fn = OP_MAP[cmd]; - if (!fn) { - // invalid content command, trying to recover - var cmds = splitCombinedOperations(cmd); - if (cmds) { - cmd = cmds[0]; - fn = OP_MAP[cmd]; - // feeding other command on the next interation - hasNextObj = true; - nextObj = Cmd.get(cmds[1]); - } - } assertWellFormed(fn, 'Unknown command "' + cmd + '"'); // TODO figure out how to type-check vararg functions @@ -12907,6 +13454,18 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { value[1] ]); break; + case 'BM': + // We support the default so don't trigger the TODO. + if (!isName(value) || value.name != 'Normal') + TODO('graphic state operator ' + key); + break; + case 'SMask': + // We support the default so don't trigger the TODO. + if (!isName(value) || value.name != 'None') + TODO('graphic state operator ' + key); + break; + // Only generate info log messages for the following since + // they are unlikey to have a big impact on the rendering. case 'OP': case 'op': case 'OPM': @@ -12919,14 +13478,13 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { case 'HT': case 'SM': case 'SA': - case 'BM': - case 'SMask': case 'AIS': case 'TK': - TODO('graphic state operator ' + key); + // TODO implement these operators. + info('graphic state operator ' + key); break; default: - warn('Unknown graphic state operator ' + key); + info('Unknown graphic state operator ' + key); break; } } @@ -12940,13 +13498,88 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { args = []; } else if (obj != null) { assertWellFormed(args.length <= 33, 'Too many arguments'); - args.push(obj); + args.push(obj instanceof Dict ? obj.getAll() : obj); } } return queue; }, + getTextContent: function partialEvaluatorGetIRQueue(stream, resources) { + + var self = this; + var xref = this.xref; + + function handleSetFont(fontName, fontRef) { + var fontRes = resources.get('Font'); + + // TODO: TOASK: Is it possible to get here? If so, what does + // args[0].name should be like??? + assert(fontRes, 'fontRes not available'); + + fontRes = xref.fetchIfRef(fontRes); + fontRef = fontRef || fontRes.get(fontName); + var font = xref.fetchIfRef(fontRef), tra; + assertWellFormed(isDict(font)); + if (!font.translated) { + font.translated = self.translateFont(font, xref, resources); + } + return font; + } + + resources = xref.fetchIfRef(resources) || new Dict(); + + var parser = new Parser(new Lexer(stream), false); + var res = resources; + var args = [], obj; + + var text = ''; + var chunk = ''; + var font = null; + while (!isEOF(obj = parser.getObj())) { + if (isCmd(obj)) { + var cmd = obj.cmd; + switch (cmd) { + case 'Tf': + font = handleSetFont(args[0].name); + break; + case 'TJ': + var items = args[0]; + for (var j = 0, jj = items.length; j < jj; j++) { + if (typeof items[j] === 'string') { + chunk += items[j]; + } else if (items[j] < 0) { + // making all negative offsets a space - better to have + // a space in incorrect place than not have them at all + chunk += ' '; + } + } + break; + case 'Tj': + chunk += args[0]; + break; + case "'": + chunk += args[0] + ' '; + break; + case '"': + chunk += args[2] + ' '; + break; + } // switch + if (chunk !== '') { + text += fontCharsToUnicode(chunk, font.translated.properties); + chunk = ''; + } + + args = []; + } else if (obj != null) { + assertWellFormed(args.length <= 33, 'Too many arguments'); + args.push(obj); + } + } + + return text; + }, + extractDataStructures: function partialEvaluatorExtractDataStructures(dict, baseDict, xref, properties) { @@ -12954,7 +13587,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { var toUnicode = dict.get('ToUnicode') || baseDict.get('ToUnicode'); if (toUnicode) - properties.toUnicode = this.readToUnicode(toUnicode, xref); + properties.toUnicode = this.readToUnicode(toUnicode, xref, properties); if (properties.composite) { // CIDSystemInfo helps to match CID to glyphs @@ -13010,7 +13643,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { properties.hasEncoding = hasEncoding; }, - readToUnicode: function PartialEvaluator_readToUnicode(toUnicode, xref) { + readToUnicode: function PartialEvaluator_readToUnicode(toUnicode, xref, + properties) { var cmapObj = toUnicode; var charToUnicode = []; if (isName(cmapObj)) { @@ -13099,6 +13733,11 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { } } else if (octet == 0x3E) { if (token.length) { + // Heuristic: guessing chars size by checking numbers sizes + // in the CMap entries. + if (token.length == 2 && properties.composite) + properties.wideChars = false; + if (token.length <= 4) { // parsing hex number tokens.push(parseInt(token, 16)); @@ -13316,6 +13955,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { length1: length1, length2: length2, composite: composite, + wideChars: composite, fixedPitch: false, fontMatrix: dict.get('FontMatrix') || IDENTITY_MATRIX, firstChar: firstChar || 0, @@ -13336,7 +13976,6 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { properties.coded = true; var charProcs = dict.get('CharProcs').getAll(); var fontResources = dict.get('Resources') || resources; - properties.resources = fontResources; properties.charProcOperatorList = {}; for (var key in charProcs) { var glyphStream = charProcs[key]; @@ -13380,6 +14019,7 @@ var EvalState = (function EvalStateClosure() { return EvalState; })(); + /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ @@ -14135,6 +14775,736 @@ function isSpecialUnicode(unicode) { unicode < kCmapGlyphOffset + kSizeOfGlyphArea); } +// The normalization table is obtained by filtering the Unicode characters +// database with entries. +var NormalizedUnicodes = { + '\u00A8': '\u0020\u0308', + '\u00AF': '\u0020\u0304', + '\u00B4': '\u0020\u0301', + '\u00B5': '\u03BC', + '\u00B8': '\u0020\u0327', + '\u0132': '\u0049\u004A', + '\u0133': '\u0069\u006A', + '\u013F': '\u004C\u00B7', + '\u0140': '\u006C\u00B7', + '\u0149': '\u02BC\u006E', + '\u017F': '\u0073', + '\u01C4': '\u0044\u017D', + '\u01C5': '\u0044\u017E', + '\u01C6': '\u0064\u017E', + '\u01C7': '\u004C\u004A', + '\u01C8': '\u004C\u006A', + '\u01C9': '\u006C\u006A', + '\u01CA': '\u004E\u004A', + '\u01CB': '\u004E\u006A', + '\u01CC': '\u006E\u006A', + '\u01F1': '\u0044\u005A', + '\u01F2': '\u0044\u007A', + '\u01F3': '\u0064\u007A', + '\u02D8': '\u0020\u0306', + '\u02D9': '\u0020\u0307', + '\u02DA': '\u0020\u030A', + '\u02DB': '\u0020\u0328', + '\u02DC': '\u0020\u0303', + '\u02DD': '\u0020\u030B', + '\u037A': '\u0020\u0345', + '\u0384': '\u0020\u0301', + '\u03D0': '\u03B2', + '\u03D1': '\u03B8', + '\u03D2': '\u03A5', + '\u03D5': '\u03C6', + '\u03D6': '\u03C0', + '\u03F0': '\u03BA', + '\u03F1': '\u03C1', + '\u03F2': '\u03C2', + '\u03F4': '\u0398', + '\u03F5': '\u03B5', + '\u03F9': '\u03A3', + '\u0587': '\u0565\u0582', + '\u0675': '\u0627\u0674', + '\u0676': '\u0648\u0674', + '\u0677': '\u06C7\u0674', + '\u0678': '\u064A\u0674', + '\u0E33': '\u0E4D\u0E32', + '\u0EB3': '\u0ECD\u0EB2', + '\u0EDC': '\u0EAB\u0E99', + '\u0EDD': '\u0EAB\u0EA1', + '\u0F77': '\u0FB2\u0F81', + '\u0F79': '\u0FB3\u0F81', + '\u1E9A': '\u0061\u02BE', + '\u1FBD': '\u0020\u0313', + '\u1FBF': '\u0020\u0313', + '\u1FC0': '\u0020\u0342', + '\u1FFE': '\u0020\u0314', + '\u2002': '\u0020', + '\u2003': '\u0020', + '\u2004': '\u0020', + '\u2005': '\u0020', + '\u2006': '\u0020', + '\u2008': '\u0020', + '\u2009': '\u0020', + '\u200A': '\u0020', + '\u2017': '\u0020\u0333', + '\u2024': '\u002E', + '\u2025': '\u002E\u002E', + '\u2026': '\u002E\u002E\u002E', + '\u2033': '\u2032\u2032', + '\u2034': '\u2032\u2032\u2032', + '\u2036': '\u2035\u2035', + '\u2037': '\u2035\u2035\u2035', + '\u203C': '\u0021\u0021', + '\u203E': '\u0020\u0305', + '\u2047': '\u003F\u003F', + '\u2048': '\u003F\u0021', + '\u2049': '\u0021\u003F', + '\u2057': '\u2032\u2032\u2032\u2032', + '\u205F': '\u0020', + '\u20A8': '\u0052\u0073', + '\u2100': '\u0061\u002F\u0063', + '\u2101': '\u0061\u002F\u0073', + '\u2103': '\u00B0\u0043', + '\u2105': '\u0063\u002F\u006F', + '\u2106': '\u0063\u002F\u0075', + '\u2107': '\u0190', + '\u2109': '\u00B0\u0046', + '\u2116': '\u004E\u006F', + '\u2121': '\u0054\u0045\u004C', + '\u2135': '\u05D0', + '\u2136': '\u05D1', + '\u2137': '\u05D2', + '\u2138': '\u05D3', + '\u213B': '\u0046\u0041\u0058', + '\u2160': '\u0049', + '\u2161': '\u0049\u0049', + '\u2162': '\u0049\u0049\u0049', + '\u2163': '\u0049\u0056', + '\u2164': '\u0056', + '\u2165': '\u0056\u0049', + '\u2166': '\u0056\u0049\u0049', + '\u2167': '\u0056\u0049\u0049\u0049', + '\u2168': '\u0049\u0058', + '\u2169': '\u0058', + '\u216A': '\u0058\u0049', + '\u216B': '\u0058\u0049\u0049', + '\u216C': '\u004C', + '\u216D': '\u0043', + '\u216E': '\u0044', + '\u216F': '\u004D', + '\u2170': '\u0069', + '\u2171': '\u0069\u0069', + '\u2172': '\u0069\u0069\u0069', + '\u2173': '\u0069\u0076', + '\u2174': '\u0076', + '\u2175': '\u0076\u0069', + '\u2176': '\u0076\u0069\u0069', + '\u2177': '\u0076\u0069\u0069\u0069', + '\u2178': '\u0069\u0078', + '\u2179': '\u0078', + '\u217A': '\u0078\u0069', + '\u217B': '\u0078\u0069\u0069', + '\u217C': '\u006C', + '\u217D': '\u0063', + '\u217E': '\u0064', + '\u217F': '\u006D', + '\u222C': '\u222B\u222B', + '\u222D': '\u222B\u222B\u222B', + '\u222F': '\u222E\u222E', + '\u2230': '\u222E\u222E\u222E', + '\u2474': '\u0028\u0031\u0029', + '\u2475': '\u0028\u0032\u0029', + '\u2476': '\u0028\u0033\u0029', + '\u2477': '\u0028\u0034\u0029', + '\u2478': '\u0028\u0035\u0029', + '\u2479': '\u0028\u0036\u0029', + '\u247A': '\u0028\u0037\u0029', + '\u247B': '\u0028\u0038\u0029', + '\u247C': '\u0028\u0039\u0029', + '\u247D': '\u0028\u0031\u0030\u0029', + '\u247E': '\u0028\u0031\u0031\u0029', + '\u247F': '\u0028\u0031\u0032\u0029', + '\u2480': '\u0028\u0031\u0033\u0029', + '\u2481': '\u0028\u0031\u0034\u0029', + '\u2482': '\u0028\u0031\u0035\u0029', + '\u2483': '\u0028\u0031\u0036\u0029', + '\u2484': '\u0028\u0031\u0037\u0029', + '\u2485': '\u0028\u0031\u0038\u0029', + '\u2486': '\u0028\u0031\u0039\u0029', + '\u2487': '\u0028\u0032\u0030\u0029', + '\u2488': '\u0031\u002E', + '\u2489': '\u0032\u002E', + '\u248A': '\u0033\u002E', + '\u248B': '\u0034\u002E', + '\u248C': '\u0035\u002E', + '\u248D': '\u0036\u002E', + '\u248E': '\u0037\u002E', + '\u248F': '\u0038\u002E', + '\u2490': '\u0039\u002E', + '\u2491': '\u0031\u0030\u002E', + '\u2492': '\u0031\u0031\u002E', + '\u2493': '\u0031\u0032\u002E', + '\u2494': '\u0031\u0033\u002E', + '\u2495': '\u0031\u0034\u002E', + '\u2496': '\u0031\u0035\u002E', + '\u2497': '\u0031\u0036\u002E', + '\u2498': '\u0031\u0037\u002E', + '\u2499': '\u0031\u0038\u002E', + '\u249A': '\u0031\u0039\u002E', + '\u249B': '\u0032\u0030\u002E', + '\u249C': '\u0028\u0061\u0029', + '\u249D': '\u0028\u0062\u0029', + '\u249E': '\u0028\u0063\u0029', + '\u249F': '\u0028\u0064\u0029', + '\u24A0': '\u0028\u0065\u0029', + '\u24A1': '\u0028\u0066\u0029', + '\u24A2': '\u0028\u0067\u0029', + '\u24A3': '\u0028\u0068\u0029', + '\u24A4': '\u0028\u0069\u0029', + '\u24A5': '\u0028\u006A\u0029', + '\u24A6': '\u0028\u006B\u0029', + '\u24A7': '\u0028\u006C\u0029', + '\u24A8': '\u0028\u006D\u0029', + '\u24A9': '\u0028\u006E\u0029', + '\u24AA': '\u0028\u006F\u0029', + '\u24AB': '\u0028\u0070\u0029', + '\u24AC': '\u0028\u0071\u0029', + '\u24AD': '\u0028\u0072\u0029', + '\u24AE': '\u0028\u0073\u0029', + '\u24AF': '\u0028\u0074\u0029', + '\u24B0': '\u0028\u0075\u0029', + '\u24B1': '\u0028\u0076\u0029', + '\u24B2': '\u0028\u0077\u0029', + '\u24B3': '\u0028\u0078\u0029', + '\u24B4': '\u0028\u0079\u0029', + '\u24B5': '\u0028\u007A\u0029', + '\u2A0C': '\u222B\u222B\u222B\u222B', + '\u2A74': '\u003A\u003A\u003D', + '\u2A75': '\u003D\u003D', + '\u2A76': '\u003D\u003D\u003D', + '\u2E9F': '\u6BCD', + '\u2EF3': '\u9F9F', + '\u2F00': '\u4E00', + '\u2F01': '\u4E28', + '\u2F02': '\u4E36', + '\u2F03': '\u4E3F', + '\u2F04': '\u4E59', + '\u2F05': '\u4E85', + '\u2F06': '\u4E8C', + '\u2F07': '\u4EA0', + '\u2F08': '\u4EBA', + '\u2F09': '\u513F', + '\u2F0A': '\u5165', + '\u2F0B': '\u516B', + '\u2F0C': '\u5182', + '\u2F0D': '\u5196', + '\u2F0E': '\u51AB', + '\u2F0F': '\u51E0', + '\u2F10': '\u51F5', + '\u2F11': '\u5200', + '\u2F12': '\u529B', + '\u2F13': '\u52F9', + '\u2F14': '\u5315', + '\u2F15': '\u531A', + '\u2F16': '\u5338', + '\u2F17': '\u5341', + '\u2F18': '\u535C', + '\u2F19': '\u5369', + '\u2F1A': '\u5382', + '\u2F1B': '\u53B6', + '\u2F1C': '\u53C8', + '\u2F1D': '\u53E3', + '\u2F1E': '\u56D7', + '\u2F1F': '\u571F', + '\u2F20': '\u58EB', + '\u2F21': '\u5902', + '\u2F22': '\u590A', + '\u2F23': '\u5915', + '\u2F24': '\u5927', + '\u2F25': '\u5973', + '\u2F26': '\u5B50', + '\u2F27': '\u5B80', + '\u2F28': '\u5BF8', + '\u2F29': '\u5C0F', + '\u2F2A': '\u5C22', + '\u2F2B': '\u5C38', + '\u2F2C': '\u5C6E', + '\u2F2D': '\u5C71', + '\u2F2E': '\u5DDB', + '\u2F2F': '\u5DE5', + '\u2F30': '\u5DF1', + '\u2F31': '\u5DFE', + '\u2F32': '\u5E72', + '\u2F33': '\u5E7A', + '\u2F34': '\u5E7F', + '\u2F35': '\u5EF4', + '\u2F36': '\u5EFE', + '\u2F37': '\u5F0B', + '\u2F38': '\u5F13', + '\u2F39': '\u5F50', + '\u2F3A': '\u5F61', + '\u2F3B': '\u5F73', + '\u2F3C': '\u5FC3', + '\u2F3D': '\u6208', + '\u2F3E': '\u6236', + '\u2F3F': '\u624B', + '\u2F40': '\u652F', + '\u2F41': '\u6534', + '\u2F42': '\u6587', + '\u2F43': '\u6597', + '\u2F44': '\u65A4', + '\u2F45': '\u65B9', + '\u2F46': '\u65E0', + '\u2F47': '\u65E5', + '\u2F48': '\u66F0', + '\u2F49': '\u6708', + '\u2F4A': '\u6728', + '\u2F4B': '\u6B20', + '\u2F4C': '\u6B62', + '\u2F4D': '\u6B79', + '\u2F4E': '\u6BB3', + '\u2F4F': '\u6BCB', + '\u2F50': '\u6BD4', + '\u2F51': '\u6BDB', + '\u2F52': '\u6C0F', + '\u2F53': '\u6C14', + '\u2F54': '\u6C34', + '\u2F55': '\u706B', + '\u2F56': '\u722A', + '\u2F57': '\u7236', + '\u2F58': '\u723B', + '\u2F59': '\u723F', + '\u2F5A': '\u7247', + '\u2F5B': '\u7259', + '\u2F5C': '\u725B', + '\u2F5D': '\u72AC', + '\u2F5E': '\u7384', + '\u2F5F': '\u7389', + '\u2F60': '\u74DC', + '\u2F61': '\u74E6', + '\u2F62': '\u7518', + '\u2F63': '\u751F', + '\u2F64': '\u7528', + '\u2F65': '\u7530', + '\u2F66': '\u758B', + '\u2F67': '\u7592', + '\u2F68': '\u7676', + '\u2F69': '\u767D', + '\u2F6A': '\u76AE', + '\u2F6B': '\u76BF', + '\u2F6C': '\u76EE', + '\u2F6D': '\u77DB', + '\u2F6E': '\u77E2', + '\u2F6F': '\u77F3', + '\u2F70': '\u793A', + '\u2F71': '\u79B8', + '\u2F72': '\u79BE', + '\u2F73': '\u7A74', + '\u2F74': '\u7ACB', + '\u2F75': '\u7AF9', + '\u2F76': '\u7C73', + '\u2F77': '\u7CF8', + '\u2F78': '\u7F36', + '\u2F79': '\u7F51', + '\u2F7A': '\u7F8A', + '\u2F7B': '\u7FBD', + '\u2F7C': '\u8001', + '\u2F7D': '\u800C', + '\u2F7E': '\u8012', + '\u2F7F': '\u8033', + '\u2F80': '\u807F', + '\u2F81': '\u8089', + '\u2F82': '\u81E3', + '\u2F83': '\u81EA', + '\u2F84': '\u81F3', + '\u2F85': '\u81FC', + '\u2F86': '\u820C', + '\u2F87': '\u821B', + '\u2F88': '\u821F', + '\u2F89': '\u826E', + '\u2F8A': '\u8272', + '\u2F8B': '\u8278', + '\u2F8C': '\u864D', + '\u2F8D': '\u866B', + '\u2F8E': '\u8840', + '\u2F8F': '\u884C', + '\u2F90': '\u8863', + '\u2F91': '\u897E', + '\u2F92': '\u898B', + '\u2F93': '\u89D2', + '\u2F94': '\u8A00', + '\u2F95': '\u8C37', + '\u2F96': '\u8C46', + '\u2F97': '\u8C55', + '\u2F98': '\u8C78', + '\u2F99': '\u8C9D', + '\u2F9A': '\u8D64', + '\u2F9B': '\u8D70', + '\u2F9C': '\u8DB3', + '\u2F9D': '\u8EAB', + '\u2F9E': '\u8ECA', + '\u2F9F': '\u8F9B', + '\u2FA0': '\u8FB0', + '\u2FA1': '\u8FB5', + '\u2FA2': '\u9091', + '\u2FA3': '\u9149', + '\u2FA4': '\u91C6', + '\u2FA5': '\u91CC', + '\u2FA6': '\u91D1', + '\u2FA7': '\u9577', + '\u2FA8': '\u9580', + '\u2FA9': '\u961C', + '\u2FAA': '\u96B6', + '\u2FAB': '\u96B9', + '\u2FAC': '\u96E8', + '\u2FAD': '\u9751', + '\u2FAE': '\u975E', + '\u2FAF': '\u9762', + '\u2FB0': '\u9769', + '\u2FB1': '\u97CB', + '\u2FB2': '\u97ED', + '\u2FB3': '\u97F3', + '\u2FB4': '\u9801', + '\u2FB5': '\u98A8', + '\u2FB6': '\u98DB', + '\u2FB7': '\u98DF', + '\u2FB8': '\u9996', + '\u2FB9': '\u9999', + '\u2FBA': '\u99AC', + '\u2FBB': '\u9AA8', + '\u2FBC': '\u9AD8', + '\u2FBD': '\u9ADF', + '\u2FBE': '\u9B25', + '\u2FBF': '\u9B2F', + '\u2FC0': '\u9B32', + '\u2FC1': '\u9B3C', + '\u2FC2': '\u9B5A', + '\u2FC3': '\u9CE5', + '\u2FC4': '\u9E75', + '\u2FC5': '\u9E7F', + '\u2FC6': '\u9EA5', + '\u2FC7': '\u9EBB', + '\u2FC8': '\u9EC3', + '\u2FC9': '\u9ECD', + '\u2FCA': '\u9ED1', + '\u2FCB': '\u9EF9', + '\u2FCC': '\u9EFD', + '\u2FCD': '\u9F0E', + '\u2FCE': '\u9F13', + '\u2FCF': '\u9F20', + '\u2FD0': '\u9F3B', + '\u2FD1': '\u9F4A', + '\u2FD2': '\u9F52', + '\u2FD3': '\u9F8D', + '\u2FD4': '\u9F9C', + '\u2FD5': '\u9FA0', + '\u3036': '\u3012', + '\u3038': '\u5341', + '\u3039': '\u5344', + '\u303A': '\u5345', + '\u309B': '\u0020\u3099', + '\u309C': '\u0020\u309A', + '\u3131': '\u1100', + '\u3132': '\u1101', + '\u3133': '\u11AA', + '\u3134': '\u1102', + '\u3135': '\u11AC', + '\u3136': '\u11AD', + '\u3137': '\u1103', + '\u3138': '\u1104', + '\u3139': '\u1105', + '\u313A': '\u11B0', + '\u313B': '\u11B1', + '\u313C': '\u11B2', + '\u313D': '\u11B3', + '\u313E': '\u11B4', + '\u313F': '\u11B5', + '\u3140': '\u111A', + '\u3141': '\u1106', + '\u3142': '\u1107', + '\u3143': '\u1108', + '\u3144': '\u1121', + '\u3145': '\u1109', + '\u3146': '\u110A', + '\u3147': '\u110B', + '\u3148': '\u110C', + '\u3149': '\u110D', + '\u314A': '\u110E', + '\u314B': '\u110F', + '\u314C': '\u1110', + '\u314D': '\u1111', + '\u314E': '\u1112', + '\u314F': '\u1161', + '\u3150': '\u1162', + '\u3151': '\u1163', + '\u3152': '\u1164', + '\u3153': '\u1165', + '\u3154': '\u1166', + '\u3155': '\u1167', + '\u3156': '\u1168', + '\u3157': '\u1169', + '\u3158': '\u116A', + '\u3159': '\u116B', + '\u315A': '\u116C', + '\u315B': '\u116D', + '\u315C': '\u116E', + '\u315D': '\u116F', + '\u315E': '\u1170', + '\u315F': '\u1171', + '\u3160': '\u1172', + '\u3161': '\u1173', + '\u3162': '\u1174', + '\u3163': '\u1175', + '\u3164': '\u1160', + '\u3165': '\u1114', + '\u3166': '\u1115', + '\u3167': '\u11C7', + '\u3168': '\u11C8', + '\u3169': '\u11CC', + '\u316A': '\u11CE', + '\u316B': '\u11D3', + '\u316C': '\u11D7', + '\u316D': '\u11D9', + '\u316E': '\u111C', + '\u316F': '\u11DD', + '\u3170': '\u11DF', + '\u3171': '\u111D', + '\u3172': '\u111E', + '\u3173': '\u1120', + '\u3174': '\u1122', + '\u3175': '\u1123', + '\u3176': '\u1127', + '\u3177': '\u1129', + '\u3178': '\u112B', + '\u3179': '\u112C', + '\u317A': '\u112D', + '\u317B': '\u112E', + '\u317C': '\u112F', + '\u317D': '\u1132', + '\u317E': '\u1136', + '\u317F': '\u1140', + '\u3180': '\u1147', + '\u3181': '\u114C', + '\u3182': '\u11F1', + '\u3183': '\u11F2', + '\u3184': '\u1157', + '\u3185': '\u1158', + '\u3186': '\u1159', + '\u3187': '\u1184', + '\u3188': '\u1185', + '\u3189': '\u1188', + '\u318A': '\u1191', + '\u318B': '\u1192', + '\u318C': '\u1194', + '\u318D': '\u119E', + '\u318E': '\u11A1', + '\u3200': '\u0028\u1100\u0029', + '\u3201': '\u0028\u1102\u0029', + '\u3202': '\u0028\u1103\u0029', + '\u3203': '\u0028\u1105\u0029', + '\u3204': '\u0028\u1106\u0029', + '\u3205': '\u0028\u1107\u0029', + '\u3206': '\u0028\u1109\u0029', + '\u3207': '\u0028\u110B\u0029', + '\u3208': '\u0028\u110C\u0029', + '\u3209': '\u0028\u110E\u0029', + '\u320A': '\u0028\u110F\u0029', + '\u320B': '\u0028\u1110\u0029', + '\u320C': '\u0028\u1111\u0029', + '\u320D': '\u0028\u1112\u0029', + '\u320E': '\u0028\u1100\u1161\u0029', + '\u320F': '\u0028\u1102\u1161\u0029', + '\u3210': '\u0028\u1103\u1161\u0029', + '\u3211': '\u0028\u1105\u1161\u0029', + '\u3212': '\u0028\u1106\u1161\u0029', + '\u3213': '\u0028\u1107\u1161\u0029', + '\u3214': '\u0028\u1109\u1161\u0029', + '\u3215': '\u0028\u110B\u1161\u0029', + '\u3216': '\u0028\u110C\u1161\u0029', + '\u3217': '\u0028\u110E\u1161\u0029', + '\u3218': '\u0028\u110F\u1161\u0029', + '\u3219': '\u0028\u1110\u1161\u0029', + '\u321A': '\u0028\u1111\u1161\u0029', + '\u321B': '\u0028\u1112\u1161\u0029', + '\u321C': '\u0028\u110C\u116E\u0029', + '\u321D': '\u0028\u110B\u1169\u110C\u1165\u11AB\u0029', + '\u321E': '\u0028\u110B\u1169\u1112\u116E\u0029', + '\u3220': '\u0028\u4E00\u0029', + '\u3221': '\u0028\u4E8C\u0029', + '\u3222': '\u0028\u4E09\u0029', + '\u3223': '\u0028\u56DB\u0029', + '\u3224': '\u0028\u4E94\u0029', + '\u3225': '\u0028\u516D\u0029', + '\u3226': '\u0028\u4E03\u0029', + '\u3227': '\u0028\u516B\u0029', + '\u3228': '\u0028\u4E5D\u0029', + '\u3229': '\u0028\u5341\u0029', + '\u322A': '\u0028\u6708\u0029', + '\u322B': '\u0028\u706B\u0029', + '\u322C': '\u0028\u6C34\u0029', + '\u322D': '\u0028\u6728\u0029', + '\u322E': '\u0028\u91D1\u0029', + '\u322F': '\u0028\u571F\u0029', + '\u3230': '\u0028\u65E5\u0029', + '\u3231': '\u0028\u682A\u0029', + '\u3232': '\u0028\u6709\u0029', + '\u3233': '\u0028\u793E\u0029', + '\u3234': '\u0028\u540D\u0029', + '\u3235': '\u0028\u7279\u0029', + '\u3236': '\u0028\u8CA1\u0029', + '\u3237': '\u0028\u795D\u0029', + '\u3238': '\u0028\u52B4\u0029', + '\u3239': '\u0028\u4EE3\u0029', + '\u323A': '\u0028\u547C\u0029', + '\u323B': '\u0028\u5B66\u0029', + '\u323C': '\u0028\u76E3\u0029', + '\u323D': '\u0028\u4F01\u0029', + '\u323E': '\u0028\u8CC7\u0029', + '\u323F': '\u0028\u5354\u0029', + '\u3240': '\u0028\u796D\u0029', + '\u3241': '\u0028\u4F11\u0029', + '\u3242': '\u0028\u81EA\u0029', + '\u3243': '\u0028\u81F3\u0029', + '\u32C0': '\u0031\u6708', + '\u32C1': '\u0032\u6708', + '\u32C2': '\u0033\u6708', + '\u32C3': '\u0034\u6708', + '\u32C4': '\u0035\u6708', + '\u32C5': '\u0036\u6708', + '\u32C6': '\u0037\u6708', + '\u32C7': '\u0038\u6708', + '\u32C8': '\u0039\u6708', + '\u32C9': '\u0031\u0030\u6708', + '\u32CA': '\u0031\u0031\u6708', + '\u32CB': '\u0031\u0032\u6708', + '\u3358': '\u0030\u70B9', + '\u3359': '\u0031\u70B9', + '\u335A': '\u0032\u70B9', + '\u335B': '\u0033\u70B9', + '\u335C': '\u0034\u70B9', + '\u335D': '\u0035\u70B9', + '\u335E': '\u0036\u70B9', + '\u335F': '\u0037\u70B9', + '\u3360': '\u0038\u70B9', + '\u3361': '\u0039\u70B9', + '\u3362': '\u0031\u0030\u70B9', + '\u3363': '\u0031\u0031\u70B9', + '\u3364': '\u0031\u0032\u70B9', + '\u3365': '\u0031\u0033\u70B9', + '\u3366': '\u0031\u0034\u70B9', + '\u3367': '\u0031\u0035\u70B9', + '\u3368': '\u0031\u0036\u70B9', + '\u3369': '\u0031\u0037\u70B9', + '\u336A': '\u0031\u0038\u70B9', + '\u336B': '\u0031\u0039\u70B9', + '\u336C': '\u0032\u0030\u70B9', + '\u336D': '\u0032\u0031\u70B9', + '\u336E': '\u0032\u0032\u70B9', + '\u336F': '\u0032\u0033\u70B9', + '\u3370': '\u0032\u0034\u70B9', + '\u33E0': '\u0031\u65E5', + '\u33E1': '\u0032\u65E5', + '\u33E2': '\u0033\u65E5', + '\u33E3': '\u0034\u65E5', + '\u33E4': '\u0035\u65E5', + '\u33E5': '\u0036\u65E5', + '\u33E6': '\u0037\u65E5', + '\u33E7': '\u0038\u65E5', + '\u33E8': '\u0039\u65E5', + '\u33E9': '\u0031\u0030\u65E5', + '\u33EA': '\u0031\u0031\u65E5', + '\u33EB': '\u0031\u0032\u65E5', + '\u33EC': '\u0031\u0033\u65E5', + '\u33ED': '\u0031\u0034\u65E5', + '\u33EE': '\u0031\u0035\u65E5', + '\u33EF': '\u0031\u0036\u65E5', + '\u33F0': '\u0031\u0037\u65E5', + '\u33F1': '\u0031\u0038\u65E5', + '\u33F2': '\u0031\u0039\u65E5', + '\u33F3': '\u0032\u0030\u65E5', + '\u33F4': '\u0032\u0031\u65E5', + '\u33F5': '\u0032\u0032\u65E5', + '\u33F6': '\u0032\u0033\u65E5', + '\u33F7': '\u0032\u0034\u65E5', + '\u33F8': '\u0032\u0035\u65E5', + '\u33F9': '\u0032\u0036\u65E5', + '\u33FA': '\u0032\u0037\u65E5', + '\u33FB': '\u0032\u0038\u65E5', + '\u33FC': '\u0032\u0039\u65E5', + '\u33FD': '\u0033\u0030\u65E5', + '\u33FE': '\u0033\u0031\u65E5', + '\uFB00': '\u0066\u0066', + '\uFB01': '\u0066\u0069', + '\uFB02': '\u0066\u006C', + '\uFB03': '\u0066\u0066\u0069', + '\uFB04': '\u0066\u0066\u006C', + '\uFB05': '\u017F\u0074', + '\uFB06': '\u0073\u0074', + '\uFB13': '\u0574\u0576', + '\uFB14': '\u0574\u0565', + '\uFB15': '\u0574\u056B', + '\uFB16': '\u057E\u0576', + '\uFB17': '\u0574\u056D', + '\uFB4F': '\u05D0\u05DC', + '\uFE49': '\u203E', + '\uFE4A': '\u203E', + '\uFE4B': '\u203E', + '\uFE4C': '\u203E', + '\uFE4D': '\u005F', + '\uFE4E': '\u005F', + '\uFE4F': '\u005F' +}; + +function fontCharsToUnicode(charCodes, fontProperties) { + var toUnicode = fontProperties.toUnicode; + var composite = fontProperties.composite; + var encoding, differences, cidToUnicode; + var result = ''; + if (composite) { + cidToUnicode = fontProperties.cidToUnicode; + for (var i = 0, ii = charCodes.length; i < ii; i += 2) { + var charCode = (charCodes.charCodeAt(i) << 8) | + charCodes.charCodeAt(i + 1); + if (toUnicode && charCode in toUnicode) { + var unicode = toUnicode[charCode]; + result += typeof unicode !== 'number' ? unicode : + String.fromCharCode(unicode); + continue; + } + result += String.fromCharCode(!cidToUnicode ? charCode : + cidToUnicode[charCode] || charCode); + } + } else { + differences = fontProperties.differences; + encoding = fontProperties.baseEncoding; + for (var i = 0, ii = charCodes.length; i < ii; i++) { + var charCode = charCodes.charCodeAt(i); + var unicode; + if (toUnicode && charCode in toUnicode) { + var unicode = toUnicode[charCode]; + result += typeof unicode !== 'number' ? unicode : + String.fromCharCode(unicode); + continue; + } + + var glyphName = charCode in differences ? differences[charCode] : + encoding[charCode]; + if (glyphName in GlyphsUnicode) { + result += String.fromCharCode(GlyphsUnicode[glyphName]); + continue; + } + result += String.fromCharCode(charCode); + } + } + // normalizing the unicode characters + for (var i = 0, ii = result.length; i < ii; i++) { + if (!(result[i] in NormalizedUnicodes)) + continue; + result = result.substring(0, i) + NormalizedUnicodes[result[i]] + + result.substring(i + 1); + ii = result.length; + } + return result; +} + /** * 'Font' is the class the outside world should use, it encapsulate all the font * decoding logics whatever type it is (assuming the font type is supported). @@ -14148,7 +15518,6 @@ var Font = (function FontClosure() { this.name = name; this.coded = properties.coded; this.charProcOperatorList = properties.charProcOperatorList; - this.resources = properties.resources; this.sizes = []; var names = name.split('+'); @@ -14172,6 +15541,7 @@ var Font = (function FontClosure() { this.widths = properties.widths; this.defaultWidth = properties.defaultWidth; this.composite = properties.composite; + this.wideChars = properties.wideChars; this.hasEncoding = properties.hasEncoding; this.fontMatrix = properties.fontMatrix; @@ -15109,6 +16479,16 @@ var Font = (function FontClosure() { properties.glyphNames = glyphNames; } + function isOS2Valid(os2Table) { + var data = os2Table.data; + // usWinAscent == 0 makes font unreadable by windows + var usWinAscent = (data[74] << 8) | data[75]; + if (usWinAscent == 0) + return false; + + return true; + } + // Check that required tables are present var requiredTables = ['OS/2', 'cmap', 'head', 'hhea', 'hmtx', 'maxp', 'name', 'post']; @@ -15116,7 +16496,7 @@ var Font = (function FontClosure() { var header = readOpenTypeHeader(font); var numTables = header.numTables; - var cmap, post, maxp, hhea, hmtx, vhea, vmtx, head, loca, glyf; + var cmap, post, maxp, hhea, hmtx, vhea, vmtx, head, loca, glyf, os2; var tables = []; for (var i = 0; i < numTables; i++) { var table = readTableEntry(font); @@ -15134,6 +16514,8 @@ var Font = (function FontClosure() { hmtx = table; else if (table.tag == 'head') head = table; + else if (table.tag == 'OS/2') + os2 = table; requiredTables.splice(index, 1); } else { @@ -15149,7 +16531,7 @@ var Font = (function FontClosure() { tables.push(table); } - var numTables = header.numTables + requiredTables.length; + var numTables = tables.length + requiredTables.length; // header and new offsets. Table entry information is appended to the // end of file. The virtualOffset represents where to put the actual @@ -15163,21 +16545,10 @@ var Font = (function FontClosure() { // of missing tables createOpenTypeHeader(header.version, ttf, numTables); - if (requiredTables.indexOf('OS/2') != -1) { - // extract some more font properties from the OpenType head and - // hhea tables; yMin and descent value are always negative - var override = { - unitsPerEm: int16([head.data[18], head.data[19]]), - yMax: int16([head.data[42], head.data[43]]), - yMin: int16([head.data[38], head.data[39]]) - 0x10000, - ascent: int16([hhea.data[4], hhea.data[5]]), - descent: int16([hhea.data[6], hhea.data[7]]) - 0x10000 - }; - - tables.push({ - tag: 'OS/2', - data: stringToArray(createOS2Table(properties, null, override)) - }); + // Invalid OS/2 can break the font for the Windows + if (os2 && !isOS2Valid(os2)) { + tables.splice(tables.indexOf(os2), 1); + os2 = null; } // Ensure the [h/v]mtx tables contains the advance width and @@ -15357,9 +16728,9 @@ var Font = (function FontClosure() { this.isSymbolicFont = false; } - // heuristics: if removed more than 2 glyphs encoding WinAnsiEncoding - // does not set properly - if (glyphsRemoved > 2) { + // heuristics: if removed more than 10 glyphs encoding WinAnsiEncoding + // does not set properly (broken PDFs have about 100 removed glyphs) + if (glyphsRemoved > 10) { warn('Switching TrueType encoding to MacRomanEncoding for ' + this.name + ' font'); encoding = Encodings.MacRomanEncoding; @@ -15458,6 +16829,23 @@ var Font = (function FontClosure() { } this.unicodeIsEnabled = unicodeIsEnabled; + if (!os2) { + // extract some more font properties from the OpenType head and + // hhea tables; yMin and descent value are always negative + var override = { + unitsPerEm: int16([head.data[18], head.data[19]]), + yMax: int16([head.data[42], head.data[43]]), + yMin: int16([head.data[38], head.data[39]]) - 0x10000, + ascent: int16([hhea.data[4], hhea.data[5]]), + descent: int16([hhea.data[6], hhea.data[7]]) - 0x10000 + }; + + tables.push({ + tag: 'OS/2', + data: stringToArray(createOS2Table(properties, glyphs, override)) + }); + } + // Rewrite the 'post' table if needed if (requiredTables.indexOf('post') != -1) { tables.push({ @@ -15842,6 +17230,7 @@ var Font = (function FontClosure() { } // MacRoman encoding address by re-encoding the cmap table + fontCharCode = glyphName in this.glyphNameMap ? this.glyphNameMap[glyphName] : GlyphsUnicode[glyphName]; break; @@ -15885,7 +17274,7 @@ var Font = (function FontClosure() { glyphs = []; - if (this.composite) { + if (this.wideChars) { // composite fonts have multi-byte strings convert the string from // single-byte to multi-byte // XXX assuming CIDFonts are two-byte - later need to extract the @@ -16841,7 +18230,7 @@ var CFFFont = (function CFFFontClosure() { this.properties = properties; var parser = new CFFParser(file, properties); - var cff = parser.parse(); + var cff = parser.parse(true); var compiler = new CFFCompiler(cff); this.readExtra(cff); try { @@ -16932,7 +18321,7 @@ var CFFParser = (function CFFParserClosure() { this.properties = properties; } CFFParser.prototype = { - parse: function CFFParser_parse() { + parse: function CFFParser_parse(normalizeCIDData) { var properties = this.properties; var cff = new CFF(); this.cff = cff; @@ -16987,6 +18376,21 @@ var CFFParser = (function CFFParserClosure() { cff.charset = charset; cff.encoding = encoding; + if (!cff.isCIDFont || !normalizeCIDData) + return cff; + + // DirectWrite does not like CID fonts data. Trying to convert/flatten + // the font data and remove CID properties. + if (cff.fdArray.length !== 1) + error('Unable to normalize CID font in CFF data'); + + var fontDict = cff.fdArray[0]; + fontDict.setByKey(17, topDict.getByName('CharStrings')); + cff.topDict = fontDict; + cff.isCIDFont = false; + delete cff.fdArray; + delete cff.fdSelect; + return cff; }, parseHeader: function CFFParser_parseHeader() { @@ -16997,7 +18401,7 @@ var CFFParser = (function CFFParserClosure() { ++offset; if (offset != 0) { - warn('cff data is shifted'); + info('cff data is shifted'); bytes = bytes.subarray(offset); this.bytes = bytes; } @@ -17585,9 +18989,9 @@ var CFFPrivateDict = (function CFFPrivateDictClosure() { [[12, 17], 'LanguageGroup', 'num', 0], [[12, 18], 'ExpansionFactor', 'num', 0.06], [[12, 19], 'initialRandomSeed', 'num', 0], - [19, 'Subrs', 'offset', null], [20, 'defaultWidthX', 'num', 0], - [21, 'nominalWidthX', 'num', 0] + [21, 'nominalWidthX', 'num', 0], + [19, 'Subrs', 'offset', null] ]; var tables = null; function CFFPrivateDict(strings) { @@ -18045,6 +19449,7 @@ var CFFCompiler = (function CFFCompilerClosure() { return CFFCompiler; })(); + /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ @@ -22257,6 +23662,7 @@ var GlyphsUnicode = { '.notdef': 0x0000 }; + /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ @@ -22274,7 +23680,7 @@ var PDFImage = (function PDFImageClosure() { var colorSpace = dict.get('ColorSpace', 'CS'); colorSpace = ColorSpace.parse(colorSpace, xref, res); var numComps = colorSpace.numComps; - handler.send('jpeg_decode', [image.getIR(), numComps], function(message) { + handler.send('JpegDecode', [image.getIR(), numComps], function(message) { var data = message.data; var stream = new Stream(data, 0, data.length, image.dict); promise.resolve(stream); @@ -22632,6 +24038,7 @@ function loadJpegStream(id, imageData, objs) { img.src = 'data:image/jpeg;base64,' + window.btoa(imageData); } + /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ @@ -25579,6 +26986,7 @@ var Metrics = { } }; + /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ @@ -25830,9 +27238,12 @@ var Parser = (function ParserClosure() { if (name == 'CCITTFaxDecode' || name == 'CCF') { return new CCITTFaxStream(stream, params); } - if (name == 'RunLengthDecode') { + if (name == 'RunLengthDecode' || name == 'RL') { return new RunLengthStream(stream); } + if (name == 'JBIG2Decode') { + error('JBIG2 image format is not currently supprted.'); + } warn('filter "' + name + '" not supported yet'); return stream; } @@ -25842,8 +27253,16 @@ var Parser = (function ParserClosure() { })(); var Lexer = (function LexerClosure() { - function Lexer(stream) { + function Lexer(stream, knownCommands) { this.stream = stream; + // The PDFs might have "glued" commands with other commands, operands or + // literals, e.g. "q1". The knownCommands is a dictionary of the valid + // commands and their prefixes. The prefixes are built the following way: + // if there a command that is a prefix of the other valid command or + // literal (e.g. 'f' and 'false') the following prefixes must be included, + // 'fa', 'fal', 'fals'. The prefixes are not needed, if the command has no + // other commands or literals as a prefix. The knowCommands is optional. + this.knownCommands = knownCommands; } Lexer.isSpace = function Lexer_isSpace(ch) { @@ -26107,12 +27526,18 @@ var Lexer = (function LexerClosure() { // command var str = ch; + var knownCommands = this.knownCommands; + var knownCommandFound = knownCommands && (str in knownCommands); while (!!(ch = stream.lookChar()) && !specialChars[ch.charCodeAt(0)]) { + // stop if known command is found and next character does not make + // the str a command + if (knownCommandFound && !((str + ch) in knownCommands)) + break; stream.skip(); if (str.length == 128) error('Command token too long: ' + str.length); - str += ch; + knownCommandFound = knownCommands && (str in knownCommands); } if (str == 'true') return true; @@ -26218,6 +27643,7 @@ var Linearization = (function LinearizationClosure() { return Linearization; })(); + /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ @@ -26330,7 +27756,7 @@ Shadings.RadialAxial = (function RadialAxialClosure() { var r1 = raw[6]; return { type: 'Pattern', - getPattern: function(ctx) { + getPattern: function RadialAxial_getPattern(ctx) { var curMatrix = ctx.mozCurrentTransform; if (curMatrix) { var userMatrix = ctx.mozCurrentTransformInverse; @@ -26521,6 +27947,7 @@ var TilingPattern = (function TilingPatternClosure() { return TilingPattern; })(); + /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ @@ -28210,7 +29637,7 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() { if (a1 > codingLine[codingPos]) { if (a1 > this.columns) { - warn('row is wrong length'); + info('row is wrong length'); this.err = true; a1 = this.columns; } @@ -28230,7 +29657,7 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() { if (a1 > codingLine[codingPos]) { if (a1 > this.columns) { - warn('row is wrong length'); + info('row is wrong length'); this.err = true; a1 = this.columns; } @@ -28240,7 +29667,7 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() { codingLine[codingPos] = a1; } else if (a1 < codingLine[codingPos]) { if (a1 < 0) { - warn('invalid code'); + info('invalid code'); this.err = true; a1 = 0; } @@ -28402,7 +29829,7 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() { this.eof = true; break; default: - warn('bad 2d code'); + info('bad 2d code'); this.addPixels(columns, 0); this.err = true; } @@ -28465,7 +29892,7 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() { for (var i = 0; i < 4; ++i) { code1 = this.lookBits(12); if (code1 != 1) - warn('bad rtc code: ' + code1); + info('bad rtc code: ' + code1); this.eatBits(12); if (this.encoding > 0) { this.lookBits(1); @@ -28588,7 +30015,7 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() { if (result[0] && result[2]) return result[1]; } - warn('Bad two dim code'); + info('Bad two dim code'); return EOF; }; @@ -28621,7 +30048,7 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() { if (result[0]) return result[1]; } - warn('bad white code'); + info('bad white code'); this.eatBits(1); return 1; }; @@ -28658,7 +30085,7 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() { if (result[0]) return result[1]; } - warn('bad black code'); + info('bad black code'); this.eatBits(1); return 1; }; @@ -28815,6 +30242,7 @@ var LZWStream = (function LZWStreamClosure() { return LZWStream; })(); + /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ @@ -28828,10 +30256,13 @@ function MessageHandler(name, comObj) { var ah = this.actionHandler = {}; ah['console_log'] = [function ahConsoleLog(data) { - console.log.apply(console, data); + console.log.apply(console, data); }]; ah['console_error'] = [function ahConsoleError(data) { - console.error.apply(console, data); + console.error.apply(console, data); + }]; + ah['_warn'] = [function ah_Warn(data) { + warn(data); }]; comObj.onmessage = function messageHandlerComObjOnMessage(event) { @@ -28902,15 +30333,68 @@ var WorkerMessageHandler = { handler.send('test', data instanceof Uint8Array); }); - handler.on('doc', function wphSetupDoc(data) { + handler.on('GetDocRequest', function wphSetupDoc(data) { // Create only the model of the PDFDoc, which is enough for // processing the content of the pdf. - pdfModel = new PDFDocModel(new Stream(data)); + var pdfData = data.data; + var pdfPassword = data.params.password; + try { + pdfModel = new PDFDocument(new Stream(pdfData), pdfPassword); + } catch (e) { + if (e instanceof PasswordException) { + if (e.code === 'needpassword') { + handler.send('NeedPassword', { + exception: e + }); + } else if (e.code === 'incorrectpassword') { + handler.send('IncorrectPassword', { + exception: e + }); + } + + return; + } else { + throw e; + } + } + var doc = { + numPages: pdfModel.numPages, + fingerprint: pdfModel.getFingerprint(), + destinations: pdfModel.catalog.destinations, + outline: pdfModel.catalog.documentOutline, + info: pdfModel.getDocumentInfo(), + metadata: pdfModel.catalog.metadata, + encrypted: !!pdfModel.xref.encrypt + }; + handler.send('GetDoc', {pdfInfo: doc}); }); - handler.on('page_request', function wphSetupPageRequest(pageNum) { - pageNum = parseInt(pageNum); + handler.on('GetPageRequest', function wphSetupGetPage(data) { + var pageNumber = data.pageIndex + 1; + var pdfPage = pdfModel.getPage(pageNumber); + var page = { + pageIndex: data.pageIndex, + rotate: pdfPage.rotate, + ref: pdfPage.ref, + view: pdfPage.view + }; + handler.send('GetPage', {pageInfo: page}); + }); + handler.on('GetData', function wphSetupGetData(data, promise) { + promise.resolve(pdfModel.stream.bytes); + }); + + handler.on('GetAnnotationsRequest', function wphSetupGetAnnotations(data) { + var pdfPage = pdfModel.getPage(data.pageIndex + 1); + handler.send('GetAnnotations', { + pageIndex: data.pageIndex, + annotations: pdfPage.getAnnotations() + }); + }); + + handler.on('RenderPageRequest', function wphSetupRenderPage(data) { + var pageNum = data.pageIndex + 1; // The following code does quite the same as // Page.prototype.startRendering, but stops at one point and sends the @@ -28947,7 +30431,7 @@ var WorkerMessageHandler = { }; } - handler.send('page_error', { + handler.send('PageError', { pageNum: pageNum, error: e }); @@ -28965,13 +30449,30 @@ var WorkerMessageHandler = { fonts[dep] = true; } } - - handler.send('page', { - pageNum: pageNum, + handler.send('RenderPage', { + pageIndex: data.pageIndex, operatorList: operatorList, depFonts: Object.keys(fonts) }); }, this); + + handler.on('GetTextContent', function wphExtractText(data, promise) { + var pageNum = data.pageIndex + 1; + var start = Date.now(); + + var textContent = ''; + try { + var page = pdfModel.getPage(pageNum); + textContent = page.extractTextContent(); + promise.resolve(textContent); + } catch (e) { + // Skip errored pages + promise.reject(e); + } + + console.log('text indexing: page=%d - time=%dms', + pageNum, Date.now() - start); + }); } }; @@ -29012,10 +30513,22 @@ var workerConsole = { if (typeof window === 'undefined') { globalScope.console = workerConsole; + // Add a logger so we can pass warnings on to the main thread, errors will + // throw an exception which will be forwarded on automatically. + PDFJS.LogManager.addLogger({ + warn: function(msg) { + postMessage({ + action: '_warn', + data: msg + }); + } + }); + var handler = new MessageHandler('worker_processor', this); WorkerMessageHandler.setup(handler); } + /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- / /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ @@ -29655,7 +31168,10 @@ var JpegImage = (function jpegImage() { tableData[z] = data[offset++]; } } else if ((quantizationTableSpec >> 4) === 1) { //16 bit - tableData[j] = readUint16(); + for (j = 0; j < 64; j++) { + var z = dctZigZag[j]; + tableData[z] = readUint16(); + } } else throw "DQT: invalid table spec"; quantizationTables[quantizationTableSpec & 15] = tableData; @@ -29670,7 +31186,8 @@ var JpegImage = (function jpegImage() { frame.precision = data[offset++]; frame.scanLines = readUint16(); frame.samplesPerLine = readUint16(); - frame.components = []; + frame.components = {}; + frame.componentsOrder = []; var componentsCount = data[offset++], componentId; var maxH = 0, maxV = 0; for (i = 0; i < componentsCount; i++) { @@ -29678,6 +31195,7 @@ var JpegImage = (function jpegImage() { var h = data[offset + 1] >> 4; var v = data[offset + 1] & 15; var qId = data[offset + 2]; + frame.componentsOrder.push(componentId); frame.components[componentId] = { h: h, v: v, @@ -29746,14 +31264,13 @@ var JpegImage = (function jpegImage() { this.jfif = jfif; this.adobe = adobe; this.components = []; - for (var id in frame.components) { - if (frame.components.hasOwnProperty(id)) { - this.components.push({ - lines: buildComponentData(frame, frame.components[id]), - scaleX: frame.components[id].h / frame.maxH, - scaleY: frame.components[id].v / frame.maxV - }); - } + for (var i = 0; i < frame.componentsOrder.length; i++) { + var component = frame.components[frame.componentsOrder[i]]; + this.components.push({ + lines: buildComponentData(frame, component), + scaleX: component.h / frame.maxH, + scaleY: component.v / frame.maxV + }); } }, getData: function getData(width, height) { @@ -29926,7 +31443,8 @@ var JpegImage = (function jpegImage() { }; return constructor; -})();/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +})(); +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ 'use strict'; @@ -31788,6 +33306,7 @@ var JpxImage = (function JpxImageClosure() { return JpxImage; })(); + /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ @@ -32220,14 +33739,35 @@ var bidi = PDFJS.bidi = (function bidiClosure() { return bidi; })(); + /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ 'use strict'; var Metadata = PDFJS.Metadata = (function MetadataClosure() { + function fixMetadata(meta) { + return meta.replace(/>\\376\\377([^<]+)/g, function(all, codes) { + var bytes = codes.replace(/\\([0-3])([0-7])([0-7])/g, + function(code, d1, d2, d3) { + return String.fromCharCode(d1 * 64 + d2 * 8 + d3 * 1); + }); + var chars = ''; + for (var i = 0; i < bytes.length; i += 2) { + var code = bytes.charCodeAt(i) * 256 + bytes.charCodeAt(i + 1); + chars += code >= 32 && code < 127 && code != 60 && code != 62 && + code != 38 && false ? String.fromCharCode(code) : + '&#x' + (0x10000 + code).toString(16).substring(1) + ';'; + } + return '>' + chars; + }); + } + function Metadata(meta) { if (typeof meta === 'string') { + // Ghostscript produces invalid metadata + meta = fixMetadata(meta); + var parser = new DOMParser(); meta = parser.parseFromString(meta, 'application/xml'); } else if (!(meta instanceof Document)) { diff --git a/apps/files_pdfviewer/js/pdfjs/compatibility.js b/apps/files_pdfviewer/js/pdfjs/compatibility.js new file mode 100644 index 0000000000..528841bb61 --- /dev/null +++ b/apps/files_pdfviewer/js/pdfjs/compatibility.js @@ -0,0 +1,340 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ + +'use strict'; + +// Checking if the typed arrays are supported +(function checkTypedArrayCompatibility() { + if (typeof Uint8Array !== 'undefined') { + // some mobile version might not support Float64Array + if (typeof Float64Array === 'undefined') + window.Float64Array = Float32Array; + + return; + } + + function subarray(start, end) { + return new TypedArray(this.slice(start, end)); + } + + function setArrayOffset(array, offset) { + if (arguments.length < 2) + offset = 0; + for (var i = 0, n = array.length; i < n; ++i, ++offset) + this[offset] = array[i] & 0xFF; + } + + function TypedArray(arg1) { + var result; + if (typeof arg1 === 'number') { + result = []; + for (var i = 0; i < arg1; ++i) + result[i] = 0; + } else + result = arg1.slice(0); + + result.subarray = subarray; + result.buffer = result; + result.byteLength = result.length; + result.set = setArrayOffset; + + if (typeof arg1 === 'object' && arg1.buffer) + result.buffer = arg1.buffer; + + return result; + } + + window.Uint8Array = TypedArray; + + // we don't need support for set, byteLength for 32-bit array + // so we can use the TypedArray as well + window.Uint32Array = TypedArray; + window.Int32Array = TypedArray; + window.Uint16Array = TypedArray; + window.Float32Array = TypedArray; + window.Float64Array = TypedArray; +})(); + +// Object.create() ? +(function checkObjectCreateCompatibility() { + if (typeof Object.create !== 'undefined') + return; + + Object.create = function objectCreate(proto) { + var constructor = function objectCreateConstructor() {}; + constructor.prototype = proto; + return new constructor(); + }; +})(); + +// Object.defineProperty() ? +(function checkObjectDefinePropertyCompatibility() { + if (typeof Object.defineProperty !== 'undefined') + return; + + Object.defineProperty = function objectDefineProperty(obj, name, def) { + delete obj[name]; + if ('get' in def) + obj.__defineGetter__(name, def['get']); + if ('set' in def) + obj.__defineSetter__(name, def['set']); + if ('value' in def) { + obj.__defineSetter__(name, function objectDefinePropertySetter(value) { + this.__defineGetter__(name, function objectDefinePropertyGetter() { + return value; + }); + return value; + }); + obj[name] = def.value; + } + }; +})(); + +// Object.keys() ? +(function checkObjectKeysCompatibility() { + if (typeof Object.keys !== 'undefined') + return; + + Object.keys = function objectKeys(obj) { + var result = []; + for (var i in obj) { + if (obj.hasOwnProperty(i)) + result.push(i); + } + return result; + }; +})(); + +// No XMLHttpRequest.response ? +(function checkXMLHttpRequestResponseCompatibility() { + var xhrPrototype = XMLHttpRequest.prototype; + if ('response' in xhrPrototype || + 'mozResponseArrayBuffer' in xhrPrototype || + 'mozResponse' in xhrPrototype || + 'responseArrayBuffer' in xhrPrototype) + return; + // IE ? + if (typeof VBArray !== 'undefined') { + Object.defineProperty(xhrPrototype, 'response', { + get: function xmlHttpRequestResponseGet() { + return new Uint8Array(new VBArray(this.responseBody).toArray()); + } + }); + Object.defineProperty(xhrPrototype, 'overrideMimeType', { + value: function xmlHttpRequestOverrideMimeType(mimeType) {} + }); + return; + } + + // other browsers + function responseTypeSetter() { + // will be only called to set "arraybuffer" + this.overrideMimeType('text/plain; charset=x-user-defined'); + } + if (typeof xhrPrototype.overrideMimeType === 'function') { + Object.defineProperty(xhrPrototype, 'responseType', + { set: responseTypeSetter }); + } + function responseGetter() { + var text = this.responseText; + var i, n = text.length; + var result = new Uint8Array(n); + for (i = 0; i < n; ++i) + result[i] = text.charCodeAt(i) & 0xFF; + return result; + } + Object.defineProperty(xhrPrototype, 'response', { get: responseGetter }); +})(); + +// window.btoa (base64 encode function) ? +(function checkWindowBtoaCompatibility() { + if ('btoa' in window) + return; + + var digits = + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; + + window.btoa = function windowBtoa(chars) { + var buffer = ''; + var i, n; + for (i = 0, n = chars.length; i < n; i += 3) { + var b1 = chars.charCodeAt(i) & 0xFF; + var b2 = chars.charCodeAt(i + 1) & 0xFF; + var b3 = chars.charCodeAt(i + 2) & 0xFF; + var d1 = b1 >> 2, d2 = ((b1 & 3) << 4) | (b2 >> 4); + var d3 = i + 1 < n ? ((b2 & 0xF) << 2) | (b3 >> 6) : 64; + var d4 = i + 2 < n ? (b3 & 0x3F) : 64; + buffer += (digits.charAt(d1) + digits.charAt(d2) + + digits.charAt(d3) + digits.charAt(d4)); + } + return buffer; + }; +})(); + +// Function.prototype.bind ? +(function checkFunctionPrototypeBindCompatibility() { + if (typeof Function.prototype.bind !== 'undefined') + return; + + Function.prototype.bind = function functionPrototypeBind(obj) { + var fn = this, headArgs = Array.prototype.slice.call(arguments, 1); + var bound = function functionPrototypeBindBound() { + var args = Array.prototype.concat.apply(headArgs, arguments); + return fn.apply(obj, args); + }; + return bound; + }; +})(); + +// IE9 text/html data URI +(function checkDocumentDocumentModeCompatibility() { + if (!('documentMode' in document) || document.documentMode !== 9) + return; + // overriding the src property + var originalSrcDescriptor = Object.getOwnPropertyDescriptor( + HTMLIFrameElement.prototype, 'src'); + Object.defineProperty(HTMLIFrameElement.prototype, 'src', { + get: function htmlIFrameElementPrototypeSrcGet() { return this.$src; }, + set: function htmlIFrameElementPrototypeSrcSet(src) { + this.$src = src; + if (src.substr(0, 14) != 'data:text/html') { + originalSrcDescriptor.set.call(this, src); + return; + } + // for text/html, using blank document and then + // document's open, write, and close operations + originalSrcDescriptor.set.call(this, 'about:blank'); + setTimeout((function htmlIFrameElementPrototypeSrcOpenWriteClose() { + var doc = this.contentDocument; + doc.open('text/html'); + doc.write(src.substr(src.indexOf(',') + 1)); + doc.close(); + }).bind(this), 0); + }, + enumerable: true + }); +})(); + +// HTMLElement dataset property +(function checkDatasetProperty() { + var div = document.createElement('div'); + if ('dataset' in div) + return; // dataset property exists + + Object.defineProperty(HTMLElement.prototype, 'dataset', { + get: function() { + if (this._dataset) + return this._dataset; + + var dataset = {}; + for (var j = 0, jj = this.attributes.length; j < jj; j++) { + var attribute = this.attributes[j]; + if (attribute.name.substring(0, 5) != 'data-') + continue; + var key = attribute.name.substring(5).replace(/\-([a-z])/g, + function(all, ch) { return ch.toUpperCase(); }); + dataset[key] = attribute.value; + } + + Object.defineProperty(this, '_dataset', { + value: dataset, + writable: false, + enumerable: false + }); + return dataset; + }, + enumerable: true + }); +})(); + +// HTMLElement classList property +(function checkClassListProperty() { + var div = document.createElement('div'); + if ('classList' in div) + return; // classList property exists + + function changeList(element, itemName, add, remove) { + var s = element.className || ''; + var list = s.split(/\s+/g); + if (list[0] == '') list.shift(); + var index = list.indexOf(itemName); + if (index < 0 && add) + list.push(itemName); + if (index >= 0 && remove) + list.splice(index, 1); + element.className = list.join(' '); + } + + var classListPrototype = { + add: function(name) { + changeList(this.element, name, true, false); + }, + remove: function(name) { + changeList(this.element, name, false, true); + }, + toggle: function(name) { + changeList(this.element, name, true, true); + } + }; + + Object.defineProperty(HTMLElement.prototype, 'classList', { + get: function() { + if (this._classList) + return this._classList; + + var classList = Object.create(classListPrototype, { + element: { + value: this, + writable: false, + enumerable: true + } + }); + Object.defineProperty(this, '_classList', { + value: classList, + writable: false, + enumerable: false + }); + return classList; + }, + enumerable: true + }); +})(); + +// Check console compatability +(function checkConsoleCompatibility() { + if (typeof console == 'undefined') { + console = {log: function() {}}; + } +})(); + +// Check onclick compatibility in Opera +(function checkOnClickCompatibility() { + // workaround for reported Opera bug DSK-354448: + // onclick fires on disabled buttons with opaque content + function ignoreIfTargetDisabled(event) { + if (isDisabled(event.target)) { + event.stopPropagation(); + } + } + function isDisabled(node) { + return node.disabled || (node.parentNode && isDisabled(node.parentNode)); + } + if (navigator.userAgent.indexOf('Opera') != -1) { + // use browser detection since we cannot feature-check this bug + document.addEventListener('click', ignoreIfTargetDisabled, true); + } +})(); + +// Checks if navigator.language is supported +(function checkNavigatorLanguage() { + if ('language' in navigator) + return; + Object.defineProperty(navigator, 'language', { + get: function navigatorLanguage() { + var language = navigator.userLanguage || 'en-US'; + return language.substring(0, 2).toLowerCase() + + language.substring(2).toUpperCase(); + }, + enumerable: true + }); +})(); diff --git a/apps/files_pdfviewer/js/pdfjs/update.sh b/apps/files_pdfviewer/js/pdfjs/update.sh index 599f633860..df100e780b 100755 --- a/apps/files_pdfviewer/js/pdfjs/update.sh +++ b/apps/files_pdfviewer/js/pdfjs/update.sh @@ -1,3 +1,6 @@ cd build rm pdf.js wget http://mozilla.github.com/pdf.js/build/pdf.js +wget http://mozilla.github.com/pdf.js/web/compatibility.js +wget http://mozilla.github.com/pdf.js/web/viewer.js + diff --git a/apps/files_pdfviewer/js/pdfjs/viewer.js b/apps/files_pdfviewer/js/pdfjs/viewer.js new file mode 100644 index 0000000000..90dd1eef02 --- /dev/null +++ b/apps/files_pdfviewer/js/pdfjs/viewer.js @@ -0,0 +1,1873 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ + +'use strict'; + +var kDefaultURL = 'compressed.tracemonkey-pldi-09.pdf'; +var kDefaultScale = 'auto'; +var kDefaultScaleDelta = 1.1; +var kUnknownScale = 0; +var kCacheSize = 20; +var kCssUnits = 96.0 / 72.0; +var kScrollbarPadding = 40; +var kMinScale = 0.25; +var kMaxScale = 4.0; +var kImageDirectory = './images/'; +var kSettingsMemory = 20; + +var mozL10n = document.mozL10n || document.webL10n; + +function getFileName(url) { + var anchor = url.indexOf('#'); + var query = url.indexOf('?'); + var end = Math.min( + anchor > 0 ? anchor : url.length, + query > 0 ? query : url.length); + return url.substring(url.lastIndexOf('/', end) + 1, end); +} + +var Cache = function cacheCache(size) { + var data = []; + this.push = function cachePush(view) { + var i = data.indexOf(view); + if (i >= 0) + data.splice(i); + data.push(view); + if (data.length > size) + data.shift().destroy(); + }; +}; + +var ProgressBar = (function ProgressBarClosure() { + + function clamp(v, min, max) { + return Math.min(Math.max(v, min), max); + } + + function ProgressBar(id, opts) { + + // Fetch the sub-elements for later + this.div = document.querySelector(id + ' .progress'); + + // Get options, with sensible defaults + this.height = opts.height || 100; + this.width = opts.width || 100; + this.units = opts.units || '%'; + this.percent = opts.percent || 0; + + // Initialize heights + this.div.style.height = this.height + this.units; + } + + ProgressBar.prototype = { + + updateBar: function ProgressBar_updateBar() { + var progressSize = this.width * this._percent / 100; + + if (this._percent > 95) + this.div.classList.add('full'); + + this.div.style.width = progressSize + this.units; + }, + + get percent() { + return this._percent; + }, + + set percent(val) { + this._percent = clamp(val, 0, 100); + this.updateBar(); + } + }; + + return ProgressBar; +})(); + +var RenderingQueue = (function RenderingQueueClosure() { + function RenderingQueue() { + this.items = []; + } + + RenderingQueue.prototype = { + enqueueDraw: function RenderingQueueEnqueueDraw(item) { + if (!item.drawingRequired()) + return; // as no redraw required, no need for queueing. + + this.items.push(item); + if (this.items.length > 1) + return; // not first item + + item.draw(this.continueExecution.bind(this)); + }, + continueExecution: function RenderingQueueContinueExecution() { + var item = this.items.shift(); + + if (this.items.length == 0) + return; // queue is empty + + item = this.items[0]; + item.draw(this.continueExecution.bind(this)); + } + }; + + return RenderingQueue; +})(); + +var FirefoxCom = (function FirefoxComClosure() { + return { + /** + * Creates an event that the extension is listening for and will + * synchronously respond to. + * NOTE: It is reccomended to use request() instead since one day we may not + * be able to synchronously reply. + * @param {String} action The action to trigger. + * @param {String} data Optional data to send. + * @return {*} The response. + */ + requestSync: function(action, data) { + var request = document.createTextNode(''); + request.setUserData('action', action, null); + request.setUserData('data', data, null); + request.setUserData('sync', true, null); + document.documentElement.appendChild(request); + + var sender = document.createEvent('Events'); + sender.initEvent('pdf.js.message', true, false); + request.dispatchEvent(sender); + var response = request.getUserData('response'); + document.documentElement.removeChild(request); + return response; + }, + /** + * Creates an event that the extension is listening for and will + * asynchronously respond by calling the callback. + * @param {String} action The action to trigger. + * @param {String} data Optional data to send. + * @param {Function} callback Optional response callback that will be called + * with one data argument. + */ + request: function(action, data, callback) { + var request = document.createTextNode(''); + request.setUserData('action', action, null); + request.setUserData('data', data, null); + request.setUserData('sync', false, null); + if (callback) { + request.setUserData('callback', callback, null); + + document.addEventListener('pdf.js.response', function listener(event) { + var node = event.target, + callback = node.getUserData('callback'), + response = node.getUserData('response'); + + document.documentElement.removeChild(node); + + document.removeEventListener('pdf.js.response', listener, false); + return callback(response); + }, false); + } + document.documentElement.appendChild(request); + + var sender = document.createEvent('HTMLEvents'); + sender.initEvent('pdf.js.message', true, false); + return request.dispatchEvent(sender); + } + }; +})(); + +// Settings Manager - This is a utility for saving settings +// First we see if localStorage is available +// If not, we use FUEL in FF +var Settings = (function SettingsClosure() { + var isLocalStorageEnabled = (function localStorageEnabledTest() { + // Feature test as per http://diveintohtml5.info/storage.html + // The additional localStorage call is to get around a FF quirk, see + // bug #495747 in bugzilla + try { + return 'localStorage' in window && window['localStorage'] !== null && + localStorage; + } catch (e) { + return false; + } + })(); + + var isFirefoxExtension = PDFJS.isFirefoxExtension; + + function Settings(fingerprint) { + var database = null; + var index; + if (isFirefoxExtension) + database = FirefoxCom.requestSync('getDatabase', null) || '{}'; + else if (isLocalStorageEnabled) + database = localStorage.getItem('database') || '{}'; + else + return false; + + database = JSON.parse(database); + if (!('files' in database)) + database.files = []; + if (database.files.length >= kSettingsMemory) + database.files.shift(); + for (var i = 0, length = database.files.length; i < length; i++) { + var branch = database.files[i]; + if (branch.fingerprint == fingerprint) { + index = i; + break; + } + } + if (typeof index != 'number') + index = database.files.push({fingerprint: fingerprint}) - 1; + this.file = database.files[index]; + this.database = database; + } + + Settings.prototype = { + set: function settingsSet(name, val) { + if (!('file' in this)) + return false; + + var file = this.file; + file[name] = val; + var database = JSON.stringify(this.database); + if (isFirefoxExtension) + FirefoxCom.requestSync('setDatabase', database); + else if (isLocalStorageEnabled) + localStorage.setItem('database', database); + }, + + get: function settingsGet(name, defaultValue) { + if (!('file' in this)) + return defaultValue; + + return this.file[name] || defaultValue; + } + }; + + return Settings; +})(); + +var cache = new Cache(kCacheSize); +var renderingQueue = new RenderingQueue(); +var currentPageNumber = 1; + +var PDFView = { + pages: [], + thumbnails: [], + currentScale: kUnknownScale, + currentScaleValue: null, + initialBookmark: document.location.hash.substring(1), + startedTextExtraction: false, + pageText: [], + container: null, + initialized: false, + fellback: false, + pdfDocument: null, + // called once when the document is loaded + initialize: function pdfViewInitialize() { + this.container = document.getElementById('viewer'); + this.initialized = true; + }, + + setScale: function pdfViewSetScale(val, resetAutoSettings) { + if (val == this.currentScale) + return; + + var pages = this.pages; + for (var i = 0; i < pages.length; i++) + pages[i].update(val * kCssUnits); + + if (this.currentScale != val) + this.pages[this.page - 1].scrollIntoView(); + this.currentScale = val; + + var event = document.createEvent('UIEvents'); + event.initUIEvent('scalechange', false, false, window, 0); + event.scale = val; + event.resetAutoSettings = resetAutoSettings; + window.dispatchEvent(event); + }, + + parseScale: function pdfViewParseScale(value, resetAutoSettings) { + if ('custom' == value) + return; + + var scale = parseFloat(value); + this.currentScaleValue = value; + if (scale) { + this.setScale(scale, true); + return; + } + + var container = this.container; + var currentPage = this.pages[this.page - 1]; + var pageWidthScale = (container.clientWidth - kScrollbarPadding) / + currentPage.width * currentPage.scale / kCssUnits; + var pageHeightScale = (container.clientHeight - kScrollbarPadding) / + currentPage.height * currentPage.scale / kCssUnits; + switch (value) { + case 'page-actual': + this.setScale(1, resetAutoSettings); + break; + case 'page-width': + this.setScale(pageWidthScale, resetAutoSettings); + break; + case 'page-height': + this.setScale(pageHeightScale, resetAutoSettings); + break; + case 'page-fit': + this.setScale( + Math.min(pageWidthScale, pageHeightScale), resetAutoSettings); + break; + case 'auto': + this.setScale(Math.min(1.0, pageWidthScale), resetAutoSettings); + break; + } + + selectScaleOption(value); + }, + + zoomIn: function pdfViewZoomIn() { + var newScale = (this.currentScale * kDefaultScaleDelta).toFixed(2); + newScale = Math.min(kMaxScale, newScale); + this.parseScale(newScale, true); + }, + + zoomOut: function pdfViewZoomOut() { + var newScale = (this.currentScale / kDefaultScaleDelta).toFixed(2); + newScale = Math.max(kMinScale, newScale); + this.parseScale(newScale, true); + }, + + set page(val) { + var pages = this.pages; + var input = document.getElementById('pageNumber'); + if (!(0 < val && val <= pages.length)) { + var event = document.createEvent('UIEvents'); + event.initUIEvent('pagechange', false, false, window, 0); + event.pageNumber = this.page; + window.dispatchEvent(event); + return; + } + + pages[val - 1].updateStats(); + currentPageNumber = val; + var event = document.createEvent('UIEvents'); + event.initUIEvent('pagechange', false, false, window, 0); + event.pageNumber = val; + window.dispatchEvent(event); + + // checking if the this.page was called from the updateViewarea function: + // avoiding the creation of two "set page" method (internal and public) + if (updateViewarea.inProgress) + return; + + // Avoid scrolling the first page during loading + if (this.loading && val == 1) + return; + + pages[val - 1].scrollIntoView(); + }, + + get page() { + return currentPageNumber; + }, + + open: function pdfViewOpen(url, scale, password) { + var parameters = {password: password}; + if (typeof url === 'string') { // URL + this.url = url; + document.title = decodeURIComponent(getFileName(url)) || url; + parameters.url = url; + } else if (url && 'byteLength' in url) { // ArrayBuffer + parameters.data = url; + } + +// if (!PDFView.loadingBar) { +// PDFView.loadingBar = new ProgressBar('#loadingBar', {}); +// } + + this.container = document.getElementById('viewer'); + + this.pdfDocument = null; + var self = this; + self.loading = true; + PDFJS.getDocument(parameters).then( + function getDocumentCallback(pdfDocument) { + self.load(pdfDocument, scale); + self.loading = false; + }, + function getDocumentError(message, exception) { + if (exception.name === 'PasswordException') { + if (exception.code === 'needpassword') { + var promptString = mozL10n.get('request_password', null, + 'PDF is protected by a password:'); + password = prompt(promptString); + if (password && password.length > 0) { + return PDFView.open(url, scale, password); + } + } + } + + var loadingIndicator = document.getElementById('loading'); + loadingIndicator.textContent = mozL10n.get('loading_error_indicator', + null, 'Error'); + var moreInfo = { + message: message + }; + self.error(mozL10n.get('loading_error', null, + 'An error occurred while loading the PDF.'), moreInfo); + self.loading = false; + }, + function getDocumentProgress(progressData) { + self.progress(progressData.loaded / progressData.total); + } + ); + }, + + download: function pdfViewDownload() { + function noData() { + FirefoxCom.request('download', { originalUrl: url }); + } + + var url = this.url.split('#')[0]; + if (PDFJS.isFirefoxExtension) { + // Document isn't ready just try to download with the url. + if (!this.pdfDocument) { + noData(); + return; + } + this.pdfDocument.getData().then( + function getDataSuccess(data) { + var bb = new MozBlobBuilder(); + bb.append(data.buffer); + var blobUrl = window.URL.createObjectURL( + bb.getBlob('application/pdf')); + + FirefoxCom.request('download', { blobUrl: blobUrl, originalUrl: url }, + function response(err) { + if (err) { + // This error won't really be helpful because it's likely the + // fallback won't work either (or is already open). + PDFView.error('PDF failed to download.'); + } + window.URL.revokeObjectURL(blobUrl); + } + ); + }, + noData // Error ocurred try downloading with just the url. + ); + } else { + url += '#pdfjs.action=download', '_parent'; + window.open(url, '_parent'); + } + }, + + fallback: function pdfViewFallback() { + if (!PDFJS.isFirefoxExtension) + return; + // Only trigger the fallback once so we don't spam the user with messages + // for one PDF. + if (this.fellback) + return; + this.fellback = true; + var url = this.url.split('#')[0]; + FirefoxCom.request('fallback', url, function response(download) { + if (!download) + return; + PDFView.download(); + }); + }, + + navigateTo: function pdfViewNavigateTo(dest) { + if (typeof dest === 'string') + dest = this.destinations[dest]; + if (!(dest instanceof Array)) + return; // invalid destination + // dest array looks like that: + var destRef = dest[0]; + var pageNumber = destRef instanceof Object ? + this.pagesRefMap[destRef.num + ' ' + destRef.gen + ' R'] : (destRef + 1); + if (pageNumber > this.pages.length) + pageNumber = this.pages.length; + if (pageNumber) { + this.page = pageNumber; + var currentPage = this.pages[pageNumber - 1]; + currentPage.scrollIntoView(dest); + } + }, + + getDestinationHash: function pdfViewGetDestinationHash(dest) { + if (typeof dest === 'string') + return PDFView.getAnchorUrl('#' + escape(dest)); + if (dest instanceof Array) { + var destRef = dest[0]; // see navigateTo method for dest format + var pageNumber = destRef instanceof Object ? + this.pagesRefMap[destRef.num + ' ' + destRef.gen + ' R'] : + (destRef + 1); + if (pageNumber) { + var pdfOpenParams = PDFView.getAnchorUrl('#page=' + pageNumber); + var destKind = dest[1]; + if (typeof destKind === 'object' && 'name' in destKind && + destKind.name == 'XYZ') { + var scale = (dest[4] || this.currentScale); + pdfOpenParams += '&zoom=' + (scale * 100); + if (dest[2] || dest[3]) { + pdfOpenParams += ',' + (dest[2] || 0) + ',' + (dest[3] || 0); + } + } + return pdfOpenParams; + } + } + return ''; + }, + + /** + * For the firefox extension we prefix the full url on anchor links so they + * don't come up as resource:// urls and so open in new tab/window works. + * @param {String} anchor The anchor hash include the #. + */ + getAnchorUrl: function getAnchorUrl(anchor) { + if (PDFJS.isFirefoxExtension) + return this.url.split('#')[0] + anchor; + return anchor; + }, + + /** + * Show the error box. + * @param {String} message A message that is human readable. + * @param {Object} moreInfo (optional) Further information about the error + * that is more technical. Should have a 'message' + * and optionally a 'stack' property. + */ + error: function pdfViewError(message, moreInfo) { + var moreInfoText = mozL10n.get('error_build', {build: PDFJS.build}, + 'PDF.JS Build: {{build}}') + '\n'; + if (moreInfo) { + moreInfoText += + mozL10n.get('error_message', {message: moreInfo.message}, + 'Message: {{message}}'); + if (moreInfo.stack) { + moreInfoText += '\n' + + mozL10n.get('error_stack', {stack: moreInfo.stack}, + 'Stack: {{stack}}'); + } else { + if (moreInfo.filename) { + moreInfoText += '\n' + + mozL10n.get('error_file', {file: moreInfo.filename}, + 'File: {{file}}'); + } + if (moreInfo.lineNumber) { + moreInfoText += '\n' + + mozL10n.get('error_line', {line: moreInfo.lineNumber}, + 'Line: {{line}}'); + } + } + } + if (PDFJS.isFirefoxExtension) { + console.error(message + '\n' + moreInfoText); + this.fallback(); + return; + } + var errorWrapper = document.getElementById('errorWrapper'); + errorWrapper.removeAttribute('hidden'); + + var errorMessage = document.getElementById('errorMessage'); + errorMessage.textContent = message; + + var closeButton = document.getElementById('errorClose'); + closeButton.onclick = function() { + errorWrapper.setAttribute('hidden', 'true'); + }; + + var errorMoreInfo = document.getElementById('errorMoreInfo'); + var moreInfoButton = document.getElementById('errorShowMore'); + var lessInfoButton = document.getElementById('errorShowLess'); + moreInfoButton.onclick = function() { + errorMoreInfo.removeAttribute('hidden'); + moreInfoButton.setAttribute('hidden', 'true'); + lessInfoButton.removeAttribute('hidden'); + }; + lessInfoButton.onclick = function() { + errorMoreInfo.setAttribute('hidden', 'true'); + moreInfoButton.removeAttribute('hidden'); + lessInfoButton.setAttribute('hidden', 'true'); + }; + moreInfoButton.removeAttribute('hidden'); + lessInfoButton.setAttribute('hidden', 'true'); + errorMoreInfo.value = moreInfoText; + + errorMoreInfo.rows = moreInfoText.split('\n').length - 1; + }, + + progress: function pdfViewProgress(level) { + var percent = Math.round(level * 100); + var loadingIndicator = document.getElementById('loading'); + loadingIndicator.textContent = mozL10n.get('loading', {percent: percent}, + 'Loading... {{percent}}%'); + +// PDFView.loadingBar.percent = percent; + }, + + load: function pdfViewLoad(pdfDocument, scale) { + function bindOnAfterDraw(pageView, thumbnailView) { + // when page is painted, using the image as thumbnail base + pageView.onAfterDraw = function pdfViewLoadOnAfterDraw() { + thumbnailView.setImage(pageView.canvas); + preDraw(); + }; + } + + this.pdfDocument = pdfDocument; + +// var errorWrapper = document.getElementById('errorWrapper'); +// errorWrapper.setAttribute('hidden', 'true'); + + var loadingBox = document.getElementById('loading'); + loadingBox.setAttribute('hidden', 'true'); + +// var loadingBox = document.getElementById('loadingBox'); +// loadingBox.setAttribute('hidden', 'true'); + +// var thumbsView = document.getElementById('thumbnailView'); +// thumbsView.parentNode.scrollTop = 0; + +// while (thumbsView.hasChildNodes()) +// thumbsView.removeChild(thumbsView.lastChild); + +// if ('_loadingInterval' in thumbsView) +// clearInterval(thumbsView._loadingInterval); + + var container = document.getElementById('viewer'); + while (container.hasChildNodes()) + container.removeChild(container.lastChild); + + var pagesCount = pdfDocument.numPages; + var id = pdfDocument.fingerprint; + var storedHash = null; +// document.getElementById('numPages').textContent = +// mozL10n.get('page_of', {pageCount: pagesCount}, 'of {{pageCount}}'); + document.getElementById('numPages').innerHTML = pagesCount; + document.getElementById('pageNumber').max = pagesCount; + PDFView.documentFingerprint = id; + var store = PDFView.store = new Settings(id); + if (store.get('exists', false)) { + var page = store.get('page', '1'); + var zoom = store.get('zoom', PDFView.currentScale); + var left = store.get('scrollLeft', '0'); + var top = store.get('scrollTop', '0'); + + storedHash = 'page=' + page + '&zoom=' + zoom + ',' + left + ',' + top; + } + + var pages = this.pages = []; + this.pageText = []; + this.startedTextExtraction = false; + var pagesRefMap = {}; + var thumbnails = this.thumbnails = []; + var pagePromises = []; + for (var i = 1; i <= pagesCount; i++) + pagePromises.push(pdfDocument.getPage(i)); + var self = this; + var pagesPromise = PDFJS.Promise.all(pagePromises); + pagesPromise.then(function(promisedPages) { + for (var i = 1; i <= pagesCount; i++) { + var page = promisedPages[i - 1]; + var pageView = new PageView(container, page, i, scale, + page.stats, self.navigateTo.bind(self)); +// var thumbnailView = new ThumbnailView(thumbsView, page, i); +// bindOnAfterDraw(pageView, thumbnailView); + + pages.push(pageView); +// thumbnails.push(thumbnailView); + var pageRef = page.ref; + pagesRefMap[pageRef.num + ' ' + pageRef.gen + ' R'] = i; + } + + self.pagesRefMap = pagesRefMap; + }); + + var destinationsPromise = pdfDocument.getDestinations(); + destinationsPromise.then(function(destinations) { + self.destinations = destinations; + }); + + // outline and initial view depends on destinations and pagesRefMap + PDFJS.Promise.all([pagesPromise, destinationsPromise]).then(function() { + pdfDocument.getOutline().then(function(outline) { + self.outline = new DocumentOutlineView(outline); + }); + + self.setInitialView(storedHash, scale); + }); + + pdfDocument.getMetadata().then(function(data) { + var info = data.info, metadata = data.metadata; + self.documentInfo = info; + self.metadata = metadata; + + var pdfTitle; + if (metadata) { + if (metadata.has('dc:title')) + pdfTitle = metadata.get('dc:title'); + } + + if (!pdfTitle && info && info['Title']) + pdfTitle = info['Title']; + + if (pdfTitle) + document.title = pdfTitle + ' - ' + document.title; + }); + }, + + setInitialView: function pdfViewSetInitialView(storedHash, scale) { + // Reset the current scale, as otherwise the page's scale might not get + // updated if the zoom level stayed the same. + this.currentScale = 0; + this.currentScaleValue = null; + if (this.initialBookmark) { + this.setHash(this.initialBookmark); + this.initialBookmark = null; + } + else if (storedHash) + this.setHash(storedHash); + else if (scale) { + this.parseScale(scale, true); + this.page = 1; + } + + if (PDFView.currentScale === kUnknownScale) { + // Scale was not initialized: invalid bookmark or scale was not specified. + // Setting the default one. + this.parseScale(kDefaultScale, true); + } + }, + + search: function pdfViewStartSearch() { + // Limit this function to run every ms. + var SEARCH_TIMEOUT = 250; + var lastSeach = this.lastSearch; + var now = Date.now(); + if (lastSeach && (now - lastSeach) < SEARCH_TIMEOUT) { + if (!this.searchTimer) { + this.searchTimer = setTimeout(function resumeSearch() { + PDFView.search(); + }, + SEARCH_TIMEOUT - (now - lastSeach) + ); + } + return; + } + this.searchTimer = null; + this.lastSearch = now; + + function bindLink(link, pageNumber) { + link.href = '#' + pageNumber; + link.onclick = function searchBindLink() { + PDFView.page = pageNumber; + return false; + }; + } + + var searchResults = document.getElementById('searchResults'); + + var searchTermsInput = document.getElementById('searchTermsInput'); + searchResults.removeAttribute('hidden'); + searchResults.textContent = ''; + + var terms = searchTermsInput.value; + + if (!terms) + return; + + // simple search: removing spaces and hyphens, then scanning every + terms = terms.replace(/\s-/g, '').toLowerCase(); + var index = PDFView.pageText; + var pageFound = false; + for (var i = 0, ii = index.length; i < ii; i++) { + var pageText = index[i].replace(/\s-/g, '').toLowerCase(); + var j = pageText.indexOf(terms); + if (j < 0) + continue; + + var pageNumber = i + 1; + var textSample = index[i].substr(j, 50); + var link = document.createElement('a'); + bindLink(link, pageNumber); + link.textContent = 'Page ' + pageNumber + ': ' + textSample; + searchResults.appendChild(link); + + pageFound = true; + } + if (!pageFound) { + searchResults.textContent = mozL10n.get('search_terms_not_found', null, + '(Not found)'); + } + }, + + setHash: function pdfViewSetHash(hash) { + if (!hash) + return; + + if (hash.indexOf('=') >= 0) { + var params = PDFView.parseQueryString(hash); + // borrowing syntax from "Parameters for Opening PDF Files" + if ('nameddest' in params) { + PDFView.navigateTo(params.nameddest); + return; + } + if ('page' in params) { + var pageNumber = (params.page | 0) || 1; + this.page = pageNumber; + if ('zoom' in params) { + var zoomArgs = params.zoom.split(','); // scale,left,top + // building destination array + + // If the zoom value, it has to get divided by 100. If it is a string, + // it should stay as it is. + var zoomArg = zoomArgs[0]; + var zoomArgNumber = parseFloat(zoomArg); + if (zoomArgNumber) + zoomArg = zoomArgNumber / 100; + + var dest = [null, {name: 'XYZ'}, (zoomArgs[1] | 0), + (zoomArgs[2] | 0), zoomArg]; + var currentPage = this.pages[pageNumber - 1]; + currentPage.scrollIntoView(dest); + } else + this.page = params.page; // simple page + return; + } + } else if (/^\d+$/.test(hash)) // page number + this.page = hash; + else // named destination + PDFView.navigateTo(unescape(hash)); + }, + + switchSidebarView: function pdfViewSwitchSidebarView(view) { + var thumbsView = document.getElementById('thumbnailView'); + var outlineView = document.getElementById('outlineView'); + var searchView = document.getElementById('searchView'); + + var thumbsButton = document.getElementById('viewThumbnail'); + var outlineButton = document.getElementById('viewOutline'); + var searchButton = document.getElementById('viewSearch'); + + switch (view) { + case 'thumbs': + thumbsButton.classList.add('toggled'); + outlineButton.classList.remove('toggled'); + searchButton.classList.remove('toggled'); + thumbsView.classList.remove('hidden'); + outlineView.classList.add('hidden'); + searchView.classList.add('hidden'); + + updateThumbViewArea(); + break; + + case 'outline': + thumbsButton.classList.remove('toggled'); + outlineButton.classList.add('toggled'); + searchButton.classList.remove('toggled'); + thumbsView.classList.add('hidden'); + outlineView.classList.remove('hidden'); + searchView.classList.add('hidden'); + + if (outlineButton.getAttribute('disabled')) + return; + break; + + case 'search': + thumbsButton.classList.remove('toggled'); + outlineButton.classList.remove('toggled'); + searchButton.classList.add('toggled'); + thumbsView.classList.add('hidden'); + outlineView.classList.add('hidden'); + searchView.classList.remove('hidden'); + + var searchTermsInput = document.getElementById('searchTermsInput'); + searchTermsInput.focus(); + // Start text extraction as soon as the search gets displayed. + this.extractText(); + break; + } + }, + + extractText: function() { + if (this.startedTextExtraction) + return; + this.startedTextExtraction = true; + var self = this; + function extractPageText(pageIndex) { + self.pages[pageIndex].pdfPage.getTextContent().then( + function textContentResolved(textContent) { + self.pageText[pageIndex] = textContent; + self.search(); + if ((pageIndex + 1) < self.pages.length) + extractPageText(pageIndex + 1); + } + ); + }; + extractPageText(0); + }, + + getVisiblePages: function pdfViewGetVisiblePages() { + var pages = this.pages; + var kBottomMargin = 10; + var kTopPadding = 30; + var visiblePages = []; + + var currentHeight = kTopPadding + kBottomMargin; + //var container = this.container; + var container = document.getElementById('viewer'); + + // Add 1px to the scrolltop to give a little wiggle room if the math is off, + // this won't be needed if we calc current page number based off the middle + // of the screen instead of the top. + var containerTop = container.scrollTop + 1; + for (var i = 1; i <= pages.length; ++i) { + var page = pages[i - 1]; + var pageHeight = page.height + kBottomMargin; + if (currentHeight + pageHeight > containerTop) + break; + + currentHeight += pageHeight; + } + + var containerBottom = containerTop + container.clientHeight; + for (; i <= pages.length && currentHeight < containerBottom; ++i) { + var singlePage = pages[i - 1]; + visiblePages.push({ id: singlePage.id, y: currentHeight, + view: singlePage }); + currentHeight += page.height + kBottomMargin; + } + return visiblePages; + }, + + getVisibleThumbs: function pdfViewGetVisibleThumbs() { + var thumbs = this.thumbnails; + var kBottomMargin = 15; + var visibleThumbs = []; + + var view = document.getElementById('thumbnailView'); + var currentHeight = kBottomMargin; + + var top = view.scrollTop; + for (var i = 1; i <= thumbs.length; ++i) { + var thumb = thumbs[i - 1]; + var thumbHeight = thumb.height * thumb.scaleY + kBottomMargin; + if (currentHeight + thumbHeight > top) + break; + + currentHeight += thumbHeight; + } + + var bottom = top + view.clientHeight; + for (; i <= thumbs.length && currentHeight < bottom; ++i) { + var singleThumb = thumbs[i - 1]; + visibleThumbs.push({ id: singleThumb.id, y: currentHeight, + view: singleThumb }); + currentHeight += singleThumb.height * singleThumb.scaleY + kBottomMargin; + } + + return visibleThumbs; + }, + + // Helper function to parse query string (e.g. ?param1=value&parm2=...). + parseQueryString: function pdfViewParseQueryString(query) { + var parts = query.split('&'); + var params = {}; + for (var i = 0, ii = parts.length; i < parts.length; ++i) { + var param = parts[i].split('='); + var key = param[0]; + var value = param.length > 1 ? param[1] : null; + params[unescape(key)] = unescape(value); + } + return params; + } +}; + +var PageView = function pageView(container, pdfPage, id, scale, + stats, navigateTo) { + this.id = id; + this.pdfPage = pdfPage; + + this.scale = scale || 1.0; + this.viewport = this.pdfPage.getViewport(this.scale); + + var anchor = document.createElement('a'); + anchor.name = '' + this.id; + + var div = document.createElement('div'); + div.id = 'pageContainer' + this.id; + div.className = 'page'; + + container.appendChild(anchor); + container.appendChild(div); + + this.destroy = function pageViewDestroy() { + this.update(); + this.pdfPage.destroy(); + }; + + this.update = function pageViewUpdate(scale) { + this.scale = scale || this.scale; + var viewport = this.pdfPage.getViewport(this.scale); + + this.viewport = viewport; + div.style.width = viewport.width + 'px'; + div.style.height = viewport.height + 'px'; + + while (div.hasChildNodes()) + div.removeChild(div.lastChild); + div.removeAttribute('data-loaded'); + + delete this.canvas; + + this.loadingIconDiv = document.createElement('div'); + this.loadingIconDiv.className = 'loadingIcon'; + div.appendChild(this.loadingIconDiv); + }; + + Object.defineProperty(this, 'width', { + get: function PageView_getWidth() { + return this.viewport.width; + }, + enumerable: true + }); + + Object.defineProperty(this, 'height', { + get: function PageView_getHeight() { + return this.viewport.height; + }, + enumerable: true + }); + + function setupAnnotations(pdfPage, viewport) { + function bindLink(link, dest) { + link.href = PDFView.getDestinationHash(dest); + link.onclick = function pageViewSetupLinksOnclick() { + if (dest) + PDFView.navigateTo(dest); + return false; + }; + } + function createElementWithStyle(tagName, item) { + var rect = viewport.convertToViewportRectangle(item.rect); + rect = PDFJS.Util.normalizeRect(rect); + var element = document.createElement(tagName); + element.style.left = Math.floor(rect[0]) + 'px'; + element.style.top = Math.floor(rect[1]) + 'px'; + element.style.width = Math.ceil(rect[2] - rect[0]) + 'px'; + element.style.height = Math.ceil(rect[3] - rect[1]) + 'px'; + return element; + } + function createCommentAnnotation(type, item) { + var container = document.createElement('section'); + container.className = 'annotComment'; + + var image = createElementWithStyle('img', item); + var type = item.type; + var rect = viewport.convertToViewportRectangle(item.rect); + rect = PDFJS.Util.normalizeRect(rect); + image.src = kImageDirectory + 'annotation-' + type.toLowerCase() + '.svg'; + image.alt = mozL10n.get('text_annotation_type', {type: type}, + '[{{type}} Annotation]'); + var content = document.createElement('div'); + content.setAttribute('hidden', true); + var title = document.createElement('h1'); + var text = document.createElement('p'); + content.style.left = Math.floor(rect[2]) + 'px'; + content.style.top = Math.floor(rect[1]) + 'px'; + title.textContent = item.title; + + if (!item.content && !item.title) { + content.setAttribute('hidden', true); + } else { + var e = document.createElement('span'); + var lines = item.content.split('\n'); + for (var i = 0, ii = lines.length; i < ii; ++i) { + var line = lines[i]; + e.appendChild(document.createTextNode(line)); + if (i < (ii - 1)) + e.appendChild(document.createElement('br')); + } + text.appendChild(e); + image.addEventListener('mouseover', function annotationImageOver() { + content.removeAttribute('hidden'); + }, false); + + image.addEventListener('mouseout', function annotationImageOut() { + content.setAttribute('hidden', true); + }, false); + } + + content.appendChild(title); + content.appendChild(text); + container.appendChild(image); + container.appendChild(content); + + return container; + } + + pdfPage.getAnnotations().then(function(items) { + for (var i = 0; i < items.length; i++) { + var item = items[i]; + switch (item.type) { + case 'Link': + var link = createElementWithStyle('a', item); + link.href = item.url || ''; + if (!item.url) + bindLink(link, ('dest' in item) ? item.dest : null); + div.appendChild(link); + break; + case 'Text': + var comment = createCommentAnnotation(item.name, item); + if (comment) + div.appendChild(comment); + break; + case 'Widget': + // TODO: support forms + PDFView.fallback(); + break; + } + } + }); + } + + this.getPagePoint = function pageViewGetPagePoint(x, y) { + return this.viewport.convertToPdfPoint(x, y); + }; + + this.scrollIntoView = function pageViewScrollIntoView(dest) { + if (!dest) { + div.scrollIntoView(true); + return; + } + + var x = 0, y = 0; + var width = 0, height = 0, widthScale, heightScale; + var scale = 0; + switch (dest[1].name) { + case 'XYZ': + x = dest[2]; + y = dest[3]; + scale = dest[4]; + break; + case 'Fit': + case 'FitB': + scale = 'page-fit'; + break; + case 'FitH': + case 'FitBH': + y = dest[2]; + scale = 'page-width'; + break; + case 'FitV': + case 'FitBV': + x = dest[2]; + scale = 'page-height'; + break; + case 'FitR': + x = dest[2]; + y = dest[3]; + width = dest[4] - x; + height = dest[5] - y; + widthScale = (this.container.clientWidth - kScrollbarPadding) / + width / kCssUnits; + heightScale = (this.container.clientHeight - kScrollbarPadding) / + height / kCssUnits; + scale = Math.min(widthScale, heightScale); + break; + default: + return; + } + + if (scale && scale !== PDFView.currentScale) + PDFView.parseScale(scale, true); + else if (PDFView.currentScale === kUnknownScale) + PDFView.parseScale(kDefaultScale, true); + + var boundingRect = [ + this.viewport.convertToViewportPoint(x, y), + this.viewport.convertToViewportPoint(x + width, y + height) + ]; + setTimeout(function pageViewScrollIntoViewRelayout() { + // letting page to re-layout before scrolling + var scale = PDFView.currentScale; + var x = Math.min(boundingRect[0][0], boundingRect[1][0]); + var y = Math.min(boundingRect[0][1], boundingRect[1][1]); + var width = Math.abs(boundingRect[0][0] - boundingRect[1][0]); + var height = Math.abs(boundingRect[0][1] - boundingRect[1][1]); + + // using temporary div to scroll it into view + var tempDiv = document.createElement('div'); + tempDiv.style.position = 'absolute'; + tempDiv.style.left = Math.floor(x) + 'px'; + tempDiv.style.top = Math.floor(y) + 'px'; + tempDiv.style.width = Math.ceil(width) + 'px'; + tempDiv.style.height = Math.ceil(height) + 'px'; + div.appendChild(tempDiv); + tempDiv.scrollIntoView(true); + div.removeChild(tempDiv); + }, 0); + }; + + this.drawingRequired = function() { + return !div.querySelector('canvas'); + }; + + this.draw = function pageviewDraw(callback) { + if (!this.drawingRequired()) { + this.updateStats(); + callback(); + return; + } + + var canvas = document.createElement('canvas'); + canvas.id = 'page' + this.id; + canvas.mozOpaque = true; + div.appendChild(canvas); + this.canvas = canvas; + + var textLayerDiv = null; + if (!PDFJS.disableTextLayer) { + textLayerDiv = document.createElement('div'); + textLayerDiv.className = 'textLayer'; + div.appendChild(textLayerDiv); + } + var textLayer = textLayerDiv ? new TextLayerBuilder(textLayerDiv) : null; + + var scale = this.scale, viewport = this.viewport; + canvas.width = viewport.width; + canvas.height = viewport.height; + + var ctx = canvas.getContext('2d'); + ctx.save(); + ctx.fillStyle = 'rgb(255, 255, 255)'; + ctx.fillRect(0, 0, canvas.width, canvas.height); + ctx.restore(); + + // Rendering area + + var self = this; + function pageViewDrawCallback(error) { + if (self.loadingIconDiv) { + div.removeChild(self.loadingIconDiv); + delete self.loadingIconDiv; + } + + if (error) { + PDFView.error(mozL10n.get('rendering_error', null, + 'An error occurred while rendering the page.'), error); + } + + self.stats = pdfPage.stats; + self.updateStats(); + if (self.onAfterDraw) + self.onAfterDraw(); + + cache.push(self); + callback(); + } + + var renderContext = { + canvasContext: ctx, + viewport: this.viewport, + textLayer: textLayer + }; + this.pdfPage.render(renderContext).then( + function pdfPageRenderCallback() { + pageViewDrawCallback(null); + }, + function pdfPageRenderError(error) { + pageViewDrawCallback(error); + } + ); + + setupAnnotations(this.pdfPage, this.viewport); + div.setAttribute('data-loaded', true); + }; + + this.updateStats = function pageViewUpdateStats() { + if (PDFJS.pdfBug && Stats.enabled) { + var stats = this.stats; + Stats.add(this.id, stats); + } + }; +}; + +var ThumbnailView = function thumbnailView(container, pdfPage, id) { + var anchor = document.createElement('a'); + anchor.href = PDFView.getAnchorUrl('#page=' + id); + anchor.title = mozL10n.get('thumb_page_title', {page: id}, 'Page {{page}}'); + anchor.onclick = function stopNavigation() { + PDFView.page = id; + return false; + }; + + var viewport = pdfPage.getViewport(1); + var pageWidth = this.width = viewport.width; + var pageHeight = this.height = viewport.height; + var pageRatio = pageWidth / pageHeight; + this.id = id; + + var canvasWidth = 98; + var canvasHeight = canvasWidth / this.width * this.height; + var scaleX = this.scaleX = (canvasWidth / pageWidth); + var scaleY = this.scaleY = (canvasHeight / pageHeight); + + var div = document.createElement('div'); + div.id = 'thumbnailContainer' + id; + div.className = 'thumbnail'; + + anchor.appendChild(div); + container.appendChild(anchor); + + this.hasImage = false; + + function getPageDrawContext() { + var canvas = document.createElement('canvas'); + canvas.id = 'thumbnail' + id; + canvas.mozOpaque = true; + + canvas.width = canvasWidth; + canvas.height = canvasHeight; + canvas.className = 'thumbnailImage'; + canvas.setAttribute('aria-label', mozL10n.get('thumb_page_canvas', + {page: id}, 'Thumbnail of Page {{page}}')); + + div.setAttribute('data-loaded', true); + + var ring = document.createElement('div'); + ring.className = 'thumbnailSelectionRing'; + ring.appendChild(canvas); + div.appendChild(ring); + + var ctx = canvas.getContext('2d'); + ctx.save(); + ctx.fillStyle = 'rgb(255, 255, 255)'; + ctx.fillRect(0, 0, canvasWidth, canvasHeight); + ctx.restore(); + return ctx; + } + + this.drawingRequired = function thumbnailViewDrawingRequired() { + return !this.hasImage; + }; + + this.draw = function thumbnailViewDraw(callback) { + if (this.hasImage) { + callback(); + return; + } + + var ctx = getPageDrawContext(); + var drawViewport = pdfPage.getViewport(scaleX); + var renderContext = { + canvasContext: ctx, + viewport: drawViewport + }; + pdfPage.render(renderContext).then( + function pdfPageRenderCallback() { + callback(); + }, + function pdfPageRenderError(error) { + callback(); + } + ); + this.hasImage = true; + }; + + this.setImage = function thumbnailViewSetImage(img) { + if (this.hasImage || !img) + return; + + var ctx = getPageDrawContext(); + ctx.drawImage(img, 0, 0, img.width, img.height, + 0, 0, ctx.canvas.width, ctx.canvas.height); + + this.hasImage = true; + }; +}; + +var DocumentOutlineView = function documentOutlineView(outline) { + var outlineView = document.getElementById('outlineView'); +// while (outlineView.firstChild) +// outlineView.removeChild(outlineView.firstChild); + + function bindItemLink(domObj, item) { + domObj.href = PDFView.getDestinationHash(item.dest); + domObj.onclick = function documentOutlineViewOnclick(e) { + PDFView.navigateTo(item.dest); + return false; + }; + } + + if (!outline) { + var noOutline = document.createElement('div'); + noOutline.classList.add('noOutline'); + //noOutline.textContent = mozL10n.get('no_outline', null, + // 'No Outline Available'); + noOutline.textContent = 'No Outline Available'; + //outlineView.appendChild(noOutline); + return; + } + + var queue = [{parent: outlineView, items: outline}]; + while (queue.length > 0) { + var levelData = queue.shift(); + var i, n = levelData.items.length; + for (i = 0; i < n; i++) { + var item = levelData.items[i]; + var div = document.createElement('div'); + div.className = 'outlineItem'; + var a = document.createElement('a'); + bindItemLink(a, item); + a.textContent = item.title; + div.appendChild(a); + + if (item.items.length > 0) { + var itemsDiv = document.createElement('div'); + itemsDiv.className = 'outlineItems'; + div.appendChild(itemsDiv); + queue.push({parent: itemsDiv, items: item.items}); + } + +// levelData.parent.appendChild(div); + } + } +}; + +// optimised CSS custom property getter/setter +var CustomStyle = (function CustomStyleClosure() { + + // As noted on: http://www.zachstronaut.com/posts/2009/02/17/ + // animate-css-transforms-firefox-webkit.html + // in some versions of IE9 it is critical that ms appear in this list + // before Moz + var prefixes = ['ms', 'Moz', 'Webkit', 'O']; + var _cache = { }; + + function CustomStyle() { + } + + CustomStyle.getProp = function get(propName, element) { + // check cache only when no element is given + if (arguments.length == 1 && typeof _cache[propName] == 'string') { + return _cache[propName]; + } + + element = element || document.documentElement; + var style = element.style, prefixed, uPropName; + + // test standard property first + if (typeof style[propName] == 'string') { + return (_cache[propName] = propName); + } + + // capitalize + uPropName = propName.charAt(0).toUpperCase() + propName.slice(1); + + // test vendor specific properties + for (var i = 0, l = prefixes.length; i < l; i++) { + prefixed = prefixes[i] + uPropName; + if (typeof style[prefixed] == 'string') { + return (_cache[propName] = prefixed); + } + } + + //if all fails then set to undefined + return (_cache[propName] = 'undefined'); + } + + CustomStyle.setProp = function set(propName, element, str) { + var prop = this.getProp(propName); + if (prop != 'undefined') + element.style[prop] = str; + } + + return CustomStyle; +})(); + +var TextLayerBuilder = function textLayerBuilder(textLayerDiv) { + this.textLayerDiv = textLayerDiv; + + this.beginLayout = function textLayerBuilderBeginLayout() { + this.textDivs = []; + this.textLayerQueue = []; + }; + + this.endLayout = function textLayerBuilderEndLayout() { + var self = this; + var textDivs = this.textDivs; + var textLayerDiv = this.textLayerDiv; + var renderTimer = null; + var renderingDone = false; + var renderInterval = 0; + var resumeInterval = 500; // in ms + + // Render the text layer, one div at a time + function renderTextLayer() { + if (textDivs.length === 0) { + clearInterval(renderTimer); + renderingDone = true; + return; + } + var textDiv = textDivs.shift(); + if (textDiv.dataset.textLength > 0) { + textLayerDiv.appendChild(textDiv); + + if (textDiv.dataset.textLength > 1) { // avoid div by zero + // Adjust div width to match canvas text + // Due to the .offsetWidth calls, this is slow + // This needs to come after appending to the DOM + var textScale = textDiv.dataset.canvasWidth / textDiv.offsetWidth; + CustomStyle.setProp('transform' , textDiv, + 'scale(' + textScale + ', 1)'); + CustomStyle.setProp('transformOrigin' , textDiv, '0% 0%'); + } + } // textLength > 0 + } + renderTimer = setInterval(renderTextLayer, renderInterval); + + // Stop rendering when user scrolls. Resume after XXX milliseconds + // of no scroll events + var scrollTimer = null; + function textLayerOnScroll() { + if (renderingDone) { + window.removeEventListener('scroll', textLayerOnScroll, false); + return; + } + + // Immediately pause rendering + clearInterval(renderTimer); + + clearTimeout(scrollTimer); + scrollTimer = setTimeout(function textLayerScrollTimer() { + // Resume rendering + renderTimer = setInterval(renderTextLayer, renderInterval); + }, resumeInterval); + }; // textLayerOnScroll + + window.addEventListener('scroll', textLayerOnScroll, false); + }; // endLayout + + this.appendText = function textLayerBuilderAppendText(text, + fontName, fontSize) { + var textDiv = document.createElement('div'); + + // vScale and hScale already contain the scaling to pixel units + var fontHeight = fontSize * text.geom.vScale; + textDiv.dataset.canvasWidth = text.canvasWidth * text.geom.hScale; + textDiv.dataset.fontName = fontName; + + textDiv.style.fontSize = fontHeight + 'px'; + textDiv.style.left = text.geom.x + 'px'; + textDiv.style.top = (text.geom.y - fontHeight) + 'px'; + textDiv.textContent = PDFJS.bidi(text, -1); + textDiv.dir = text.direction; + textDiv.dataset.textLength = text.length; + this.textDivs.push(textDiv); + }; +}; + +window.addEventListener('load', function webViewerLoad(evt) { + PDFView.initialize(); + var params = PDFView.parseQueryString(document.location.search.substring(1)); + + var file = PDFJS.isFirefoxExtension ? + window.location.toString() : params.file || kDefaultURL; + +/* if (PDFJS.isFirefoxExtension || !window.File || !window.FileReader || + !window.FileList || !window.Blob) { + document.getElementById('openFile').setAttribute('hidden', 'true'); + } else { + document.getElementById('fileInput').value = null; + } +*/ + // Special debugging flags in the hash section of the URL. + var hash = document.location.hash.substring(1); + var hashParams = PDFView.parseQueryString(hash); + + if ('disableWorker' in hashParams) + PDFJS.disableWorker = (hashParams['disableWorker'] === 'true'); + +/* if (!PDFJS.isFirefoxExtension) { + var locale = navigator.language; + if ('locale' in hashParams) + locale = hashParams['locale']; + mozL10n.language.code = locale; + } +*/ + if ('disableTextLayer' in hashParams) + PDFJS.disableTextLayer = (hashParams['disableTextLayer'] === 'true'); + + if ('pdfBug' in hashParams && + (!PDFJS.isFirefoxExtension || FirefoxCom.requestSync('pdfBugEnabled'))) { + PDFJS.pdfBug = true; + var pdfBug = hashParams['pdfBug']; + var enabled = pdfBug.split(','); + PDFBug.enable(enabled); + PDFBug.init(); + } + +/* if (!PDFJS.isFirefoxExtension || + (PDFJS.isFirefoxExtension && FirefoxCom.requestSync('searchEnabled'))) { + document.querySelector('#viewSearch').classList.remove('hidden'); + } +*/ + // Listen for warnings to trigger the fallback UI. Errors should be caught + // and call PDFView.error() so we don't need to listen for those. + PDFJS.LogManager.addLogger({ + warn: function() { + PDFView.fallback(); + } + }); + +// var thumbsView = document.getElementById('thumbnailView'); +// thumbsView.addEventListener('scroll', updateThumbViewArea, true); +/* + var mainContainer = document.getElementById('mainContainer'); + var outerContainer = document.getElementById('outerContainer'); + mainContainer.addEventListener('transitionend', function(e) { + if (e.target == mainContainer) { + var event = document.createEvent('UIEvents'); + event.initUIEvent('resize', false, false, window, 0); + window.dispatchEvent(event); + outerContainer.classList.remove('sidebarMoving'); + } + }, true); + + document.getElementById('sidebarToggle').addEventListener('click', + function() { + this.classList.toggle('toggled'); + outerContainer.classList.add('sidebarMoving'); + outerContainer.classList.toggle('sidebarOpen'); + updateThumbViewArea(); + }); + + PDFView.open(file, 0); +*/ +}, true); + +/** + * Render the next not yet visible page already such that it is + * hopefully ready once the user scrolls to it. + */ +function preDraw() { + var pages = PDFView.pages; + var visible = PDFView.getVisiblePages(); + var last = visible[visible.length - 1]; + // PageView.id is the actual page number, which is + 1 compared + // to the index in `pages`. That means, pages[last.id] is the next + // PageView instance. + if (pages[last.id] && pages[last.id].drawingRequired()) { + renderingQueue.enqueueDraw(pages[last.id]); + return; + } + // If there is nothing to draw on the next page, maybe the user + // is scrolling up, so, let's try to render the next page *before* + // the first visible page + if (pages[visible[0].id - 2]) { + renderingQueue.enqueueDraw(pages[visible[0].id - 2]); + } +} + +function updateViewarea() { + if (!PDFView.initialized) + return; + var container = document.getElementById('viewer'); + if (!container) + return; + + var visiblePages = PDFView.getVisiblePages(); + var pageToDraw; + for (var i = 0; i < visiblePages.length; i++) { + var page = visiblePages[i]; + var pageObj = PDFView.pages[page.id - 1]; + + pageToDraw |= pageObj.drawingRequired(); + renderingQueue.enqueueDraw(pageObj); + } + + if (!visiblePages.length) + return; + + // If there is no need to draw a page that is currenlty visible, preDraw the + // next page the user might scroll to. + if (!pageToDraw) { + preDraw(); + } + + updateViewarea.inProgress = true; // used in "set page" + var currentId = PDFView.page; + var firstPage = visiblePages[0]; + PDFView.page = firstPage.id; + updateViewarea.inProgress = false; + + var currentScale = PDFView.currentScale; + var currentScaleValue = PDFView.currentScaleValue; + var normalizedScaleValue = currentScaleValue == currentScale ? + currentScale * 100 : currentScaleValue; + + var pageNumber = firstPage.id; + var pdfOpenParams = '#page=' + pageNumber; + pdfOpenParams += '&zoom=' + normalizedScaleValue; + var currentPage = PDFView.pages[pageNumber - 1]; + var topLeft = currentPage.getPagePoint(PDFView.container.scrollLeft, + (PDFView.container.scrollTop - firstPage.y)); + pdfOpenParams += ',' + Math.round(topLeft[0]) + ',' + Math.round(topLeft[1]); + + var store = PDFView.store; + store.set('exists', true); + store.set('page', pageNumber); + store.set('zoom', normalizedScaleValue); + store.set('scrollLeft', Math.round(topLeft[0])); + store.set('scrollTop', Math.round(topLeft[1])); +// var href = PDFView.getAnchorUrl(pdfOpenParams); +// document.getElementById('viewBookmark').href = href; +} + +window.addEventListener('scroll', function webViewerScroll(evt) { + updateViewarea(); +}, true); + +var thumbnailTimer; + +function updateThumbViewArea() { + // Only render thumbs after pausing scrolling for this amount of time + // (makes UI more responsive) + var delay = 50; // in ms + + if (thumbnailTimer) + clearTimeout(thumbnailTimer); + + thumbnailTimer = setTimeout(function() { + var visibleThumbs = PDFView.getVisibleThumbs(); + for (var i = 0; i < visibleThumbs.length; i++) { + var thumb = visibleThumbs[i]; + renderingQueue.enqueueDraw(PDFView.thumbnails[thumb.id - 1]); + } + }, delay); +} + +window.addEventListener('resize', function webViewerResize(evt) { + if (PDFView.initialized && PDFView.active && + (document.getElementById('pageWidthOption').selected || + document.getElementById('pageFitOption').selected )) + PDFView.parseScale(document.getElementById('scaleSelect').value); + updateViewarea(); +}); + +window.addEventListener('hashchange', function webViewerHashchange(evt) { + PDFView.setHash(document.location.hash.substring(1)); +}); + +window.addEventListener('change', function webViewerChange(evt) { + var files = evt.target.files; + if (!files || files.length == 0) + return; + + // Read the local file into a Uint8Array. + var fileReader = new FileReader(); + fileReader.onload = function webViewerChangeFileReaderOnload(evt) { + var data = evt.target.result; + var buffer = new ArrayBuffer(data.length); + var uint8Array = new Uint8Array(buffer); + + for (var i = 0; i < data.length; i++) + uint8Array[i] = data.charCodeAt(i); + + PDFView.open(uint8Array, 0); + }; + + // Read as a binary string since "readAsArrayBuffer" is not yet + // implemented in Firefox. + var file = files[0]; + fileReader.readAsBinaryString(file); + document.title = file.name; + + // URL does not reflect proper document location - hiding some icons. +// document.getElementById('viewBookmark').setAttribute('hidden', 'true'); +// document.getElementById('download').setAttribute('hidden', 'true'); +}, true); + +function selectScaleOption(value) { + var options = document.getElementById('scaleSelect').options; + var predefinedValueFound = false; + for (var i = 0; i < options.length; i++) { + var option = options[i]; + if (option.value != value) { + option.selected = false; + continue; + } + option.selected = true; + predefinedValueFound = true; + } + return predefinedValueFound; +} + +window.addEventListener('localized', function localized(evt) { + document.getElementsByTagName('html')[0].dir = mozL10n.language.direction; +}, true); + +window.addEventListener('scalechange', function scalechange(evt) { + var customScaleOption = document.getElementById('customScaleOption'); + customScaleOption.selected = false; + + if (!evt.resetAutoSettings && + (document.getElementById('pageWidthOption').selected || + document.getElementById('pageFitOption').selected || + document.getElementById('pageAutoOption').selected)) { + updateViewarea(); + return; + } + + var predefinedValueFound = selectScaleOption('' + evt.scale); + if (!predefinedValueFound) { + customScaleOption.textContent = Math.round(evt.scale * 10000) / 100 + '%'; + customScaleOption.selected = true; + } + + updateViewarea(); +}, true); + +window.addEventListener('pagechange', function pagechange(evt) { + var page = evt.pageNumber; + if (document.getElementById('pageNumber').value != page) { + document.getElementById('pageNumber').value = page; + var selected = document.querySelector('.thumbnail.selected'); + if (selected) + selected.classList.remove('selected'); +/* + var thumbnail = document.getElementById('thumbnailContainer' + page); + thumbnail.classList.add('selected'); + var visibleThumbs = PDFView.getVisibleThumbs(); + var numVisibleThumbs = visibleThumbs.length; + // If the thumbnail isn't currently visible scroll it into view. + if (numVisibleThumbs > 0) { + var first = visibleThumbs[0].id; + // Account for only one thumbnail being visible. + var last = numVisibleThumbs > 1 ? + visibleThumbs[numVisibleThumbs - 1].id : first; + if (page <= first || page >= last) + thumbnail.scrollIntoView(); + } +*/ + } + document.getElementById('previous').disabled = (page <= 1); + document.getElementById('next').disabled = (page >= PDFView.pages.length); +}, true); + +// Firefox specific event, so that we can prevent browser from zooming +window.addEventListener('DOMMouseScroll', function(evt) { + if (evt.ctrlKey) { + evt.preventDefault(); + + var ticks = evt.detail; + var direction = (ticks > 0) ? 'zoomOut' : 'zoomIn'; + for (var i = 0, length = Math.abs(ticks); i < length; i++) + PDFView[direction](); + } +}, false); + + diff --git a/apps/files_pdfviewer/js/viewer.js b/apps/files_pdfviewer/js/viewer.js index 417d3a073f..29db2ea7f2 100644 --- a/apps/files_pdfviewer/js/viewer.js +++ b/apps/files_pdfviewer/js/viewer.js @@ -24,21 +24,9 @@ function showPDFviewer(dir,filename){ var oldcontent = $("#content").html(); $("#content").html(oldcontent+'
    Loading... 0%
    '); showPDFviewer.lastTitle = document.title; - if(!showPDFviewer.loaded){ - OC.addScript( 'files_pdfviewer', 'pdfjs/build/pdf',function(){ - OC.addScript( 'files_pdfviewer', 'pdfview',function(){ - showPDFviewer.loaded=true; - PDFJS.workerSrc = OC.filePath('files_pdfviewer','js','pdfjs/build/pdf.js'); - PDFView.Ptitle = filename; - PDFView.open(url,1.00); - PDFView.active=true; - }); - }); - }else{ - PDFView.Ptitle = filename; - PDFView.open(url,1.00); - PDFView.active=true; - } + PDFView.Ptitle = filename; + PDFView.open(url,1.00); + PDFView.active=true; $("#pageWidthOption").attr("selected","selected"); showPDFviewer.shown = true; } @@ -46,14 +34,13 @@ function showPDFviewer(dir,filename){ showPDFviewer.shown=false; showPDFviewer.oldCode=''; showPDFviewer.lastTitle=''; -showPDFviewer.loaded=false; $(document).ready(function(){ if(!$.browser.msie){//doesnt work on IE if(location.href.indexOf("files")!=-1) { PDFJS.workerSrc = OC.filePath('files_pdfviewer','js','pdfjs/build/pdf.js'); if(typeof FileActions!=='undefined'){ - FileActions.register('application/pdf','Edit','',function(filename){ + FileActions.register('application/pdf','Edit', FileActions.PERMISSION_READ, '',function(filename){ showPDFviewer($('#dir').val(),filename); }); FileActions.setDefault('application/pdf','Edit'); diff --git a/apps/files_sharing/ajax/email.php b/apps/files_sharing/ajax/email.php deleted file mode 100644 index 2a81e6f982..0000000000 --- a/apps/files_sharing/ajax/email.php +++ /dev/null @@ -1,17 +0,0 @@ - array('message' => $exception->getMessage()))); -} diff --git a/apps/files_sharing/ajax/getitem.php b/apps/files_sharing/ajax/getitem.php deleted file mode 100644 index 852e5a2457..0000000000 --- a/apps/files_sharing/ajax/getitem.php +++ /dev/null @@ -1,74 +0,0 @@ - $gid, 'permissions' => $rows[$i]['permissions'], 'users' => OC_Group::usersInGroup($gid), 'parentFolder' => false)); - } else { - $group = array(array('gid' => $gid, 'permissions' => $rows[$i]['permissions'], 'users' => OC_Group::usersInGroup($gid), 'parentFolder' => basename($path))); - } - if (!isset($item['groups'])) { - $item['groups'] = $group; - } else if (is_array($item['groups'])) { - $gidExists = false; - $currentGroups = $item['groups']; - // Check if the group is already included - foreach ($currentGroups as $g) { - if ($g['gid'] == $gid) { - $gidExists = true; - } - } - if (!$gidExists) { - $item['groups'] = array_merge($item['groups'], $group); - } - } - } else { - if ($path == $source) { - $user = array(array('uid' => $uid_shared_with, 'permissions' => $rows[$i]['permissions'], 'parentFolder' => false)); - } else { - $user = array(array('uid' => $uid_shared_with, 'permissions' => $rows[$i]['permissions'], 'parentFolder' => basename($path))); - } - if (!isset($item['users'])) { - $item['users'] = $user; - } else if (is_array($item['users'])) { - $item['users'] = array_merge($item['users'], $user); - } - } - } - } - } - $path = dirname($path); -} - -OCP\JSON::success(array('data' => $item)); - -?> diff --git a/apps/files_sharing/ajax/getstatuses.php b/apps/files_sharing/ajax/getstatuses.php deleted file mode 100644 index 8edcb21475..0000000000 --- a/apps/files_sharing/ajax/getstatuses.php +++ /dev/null @@ -1,25 +0,0 @@ - $items)); - -?> \ No newline at end of file diff --git a/apps/files_sharing/ajax/setpermissions.php b/apps/files_sharing/ajax/setpermissions.php deleted file mode 100644 index 13daab738d..0000000000 --- a/apps/files_sharing/ajax/setpermissions.php +++ /dev/null @@ -1,15 +0,0 @@ - diff --git a/apps/files_sharing/ajax/share.php b/apps/files_sharing/ajax/share.php deleted file mode 100644 index fb28caf7b7..0000000000 --- a/apps/files_sharing/ajax/share.php +++ /dev/null @@ -1,40 +0,0 @@ - $shared->getToken())); - } else { - OCP\JSON::success(); - } - } catch (Exception $exception) { - OCP\Util::writeLog('files_sharing', 'Unexpected Error : '.$exception->getMessage(), OCP\Util::ERROR); - OCP\JSON::error(array('data' => array('message' => $exception->getMessage()))); - } - } else { - if ($file['encrypted'] == true) { - OCP\JSON::error(array('data' => array('message' => 'Encrypted files cannot be shared'))); - } else { - OCP\Util::writeLog('files_sharing', 'File does not exist or is not readable :'.$source, OCP\Util::ERROR); - OCP\JSON::error(array('data' => array('message' => 'File does not exist or is not readable'))); - } - } -} - -?> diff --git a/apps/files_sharing/ajax/toggleresharing.php b/apps/files_sharing/ajax/toggleresharing.php deleted file mode 100644 index ab8e82c8c3..0000000000 --- a/apps/files_sharing/ajax/toggleresharing.php +++ /dev/null @@ -1,13 +0,0 @@ - diff --git a/apps/files_sharing/ajax/unshare.php b/apps/files_sharing/ajax/unshare.php deleted file mode 100644 index d291b719e3..0000000000 --- a/apps/files_sharing/ajax/unshare.php +++ /dev/null @@ -1,14 +0,0 @@ - diff --git a/apps/files_sharing/ajax/userautocomplete.php b/apps/files_sharing/ajax/userautocomplete.php deleted file mode 100644 index 23865693a1..0000000000 --- a/apps/files_sharing/ajax/userautocomplete.php +++ /dev/null @@ -1,45 +0,0 @@ -"; -$groups[] = ""; -if(OCP\Config::getAppValue('files_sharing', 'allowSharingWithEveryone', 'no') == 'yes') { - $allGroups = OC_Group::getGroups(); - foreach($allGroups as $group) { - $groups[] = ""; - } - $allUsers = OC_User::getUsers(); - foreach($allUsers as $user) { - if($user != $self) { - $users[] = ""; - } - } -} else { - $userGroups = OC_Group::getUserGroups($self); - foreach ($userGroups as $group) { - $groupUsers = OC_Group::usersInGroup($group); - $userCount = 0; - foreach ($groupUsers as $user) { - if ($user != $self) { - $users[] = ""; - $userCount++; - } - } - // Don't include the group if only the current user is a member of it - if ($userCount > 0) { - $groups[] = ""; - } - } - $users = array_unique($users); -} -$users[] = ""; -$groups[] = ""; -$users = array_merge($users, $groups); -OCP\JSON::encodedPrint($users); - -?> diff --git a/apps/files_sharing/appinfo/app.php b/apps/files_sharing/appinfo/app.php index ea3a9da6f7..109f86b2e8 100644 --- a/apps/files_sharing/appinfo/app.php +++ b/apps/files_sharing/appinfo/app.php @@ -1,19 +1,9 @@ assign("downloadURL", OCP\Util::linkTo("", "public.php")."?service=files&token=".$token."&path="); $list->assign("readonly", true); $tmpl = new OCP\Template("files", "index", "user"); - $tmpl->assign("fileList", $list->fetchPage()); + $tmpl->assign("fileList", $list->fetchPage(), false); $tmpl->assign("breadcrumb", $breadcrumbNav->fetchPage()); $tmpl->assign("readonly", true); $tmpl->assign("allowZipDownload", false); @@ -77,6 +75,7 @@ if (isset($_GET['token']) && $source = OC_Share::getSource($_GET['token'])) { header("Content-Length: " . OC_Filesystem::filesize($source)); //download the file @ob_clean(); + OCP\Util::emitHook('OC_Share', 'public-download', array('source'=>$source, 'token'=>$token)); OC_Filesystem::readfile($source); } } else { @@ -85,4 +84,3 @@ if (isset($_GET['token']) && $source = OC_Share::getSource($_GET['token'])) { $tmpl->printPage(); die(); } -?> diff --git a/apps/files_sharing/js/list.js b/apps/files_sharing/js/list.js deleted file mode 100644 index 41eabd1f4a..0000000000 --- a/apps/files_sharing/js/list.js +++ /dev/null @@ -1,54 +0,0 @@ -$(document).ready(function() { - $( "#source" ).autocomplete({ - source: "../../files/ajax/autocomplete.php", - minLength: 1 - }); - $( "#uid_shared_with" ).autocomplete({ - source: "ajax/userautocomplete.php", - minLength: 1 - }); - $("button.delete").live('click', function( event ) { - event.preventDefault(); -// var row=$(this); - var source=$(this).attr('data-source'); - var uid_shared_with=$(this).attr('data-uid_shared_with'); - var data='source='+encodeURIComponent(source)+'&uid_shared_with='+encodeURIComponent(uid_shared_with); - $.ajax({ - type: 'GET', - url: 'ajax/unshare.php', - cache: false, - data: data -// success: function(){ -// row.remove(); -// } - }); - }); - $('#share_item').submit(function( event ){ - event.preventDefault(); - var source=$('#source').val(); - var uid_shared_with=$('#uid_shared_with').val(); - var permissions=$('#permissions').val()||0; - var data='source='+source+'&uid_shared_with='+uid_shared_with+'&permissions='+permissions; - $.ajax({ - type: 'GET', - url: 'ajax/share.php', - cache: false, - data: data, -// success: function(token){ -// if(token){ -// var html=""; -// html+=""+path+""; -// var expire=($('#expire').val())?$('#expire').val():'Never' -// html+=""+expire+"" -// html+=""+$('#baseUrl').val()+"?token="+token+"" -// html+="" -// html+="" -// $(html).insertBefore($('#newlink_row')); -// $('#expire').val(''); -// $('#expire_time').val(''); -// $('#path').val(''); -// } -// } - }); - }); -}); \ No newline at end of file diff --git a/apps/files_sharing/js/settings.js b/apps/files_sharing/js/settings.js deleted file mode 100644 index c276521b7b..0000000000 --- a/apps/files_sharing/js/settings.js +++ /dev/null @@ -1,16 +0,0 @@ -$(document).ready(function() { - $('#allowResharing').bind('change', function() { - var checked = 1; - if (!this.checked) { - checked = 0; - } - $.post(OC.filePath('files_sharing','ajax','toggleresharing.php'), 'resharing='+checked); - }); - $('#allowSharingWithEveryone').bind('change', function() { - var checked = 1; - if (!this.checked) { - checked = 0; - } - $.post(OC.filePath('files_sharing','ajax','togglesharewitheveryone.php'), 'allowSharingWithEveryone='+checked); - }); -}); \ No newline at end of file diff --git a/apps/files_sharing/js/share.js b/apps/files_sharing/js/share.js index 1ad5187354..bcfd42ce21 100644 --- a/apps/files_sharing/js/share.js +++ b/apps/files_sharing/js/share.js @@ -1,375 +1,64 @@ -OC.Share={ - icons:[], - itemUsers:[], - itemGroups:[], - itemPrivateLink:false, - usersAndGroups:[], - loadIcons:function() { - // Cache all icons for shared files - $.getJSON(OC.filePath('files_sharing', 'ajax', 'getstatuses.php'), function(result) { - if (result && result.status === 'success') { - $.each(result.data, function(item, hasPrivateLink) { - if (hasPrivateLink) { - OC.Share.icons[item] = OC.imagePath('core', 'actions/public'); - } else { - OC.Share.icons[item] = OC.imagePath('core', 'actions/shared'); - } - }); - } - }); - }, - loadItem:function(item) { - $.ajax({type: 'GET', url: OC.filePath('files_sharing', 'ajax', 'getitem.php'), data: { item: item }, async: false, success: function(result) { - if (result && result.status === 'success') { - var item = result.data; - OC.Share.itemUsers = item.users; - OC.Share.itemGroups = item.groups; - OC.Share.itemPrivateLink = item.privateLink; - } - }}); - }, - share:function(source, uid_shared_with, permissions, callback) { - $.post(OC.filePath('files_sharing', 'ajax', 'share.php'), { sources: source, uid_shared_with: uid_shared_with, permissions: permissions }, function(result) { - if (result && result.status === 'success') { - if (callback) { - callback(result.data); - } - } else { - OC.dialogs.alert(result.data.message, 'Error while sharing'); - } - }); - }, - unshare:function(source, uid_shared_with, callback) { - $.post(OC.filePath('files_sharing', 'ajax', 'unshare.php'), { source: source, uid_shared_with: uid_shared_with }, function(result) { - if (result && result.status === 'success') { - if (callback) { - callback(); - } - } else { - OC.dialogs.alert('Error', 'Error while unsharing'); - } - }); - }, - changePermissions:function(source, uid_shared_with, permissions) { - $.post(OC.filePath('files_sharing','ajax','setpermissions.php'), { source: source, uid_shared_with: uid_shared_with, permissions: permissions }, function(result) { - if (!result || result.status !== 'success') { - OC.dialogs.alert('Error', 'Error while changing permissions'); - } - }); - }, - showDropDown:function(item, appendTo) { - OC.Share.loadItem(item); - var html = '"),j=this.$getTop(b.end.row,d),i=Math.round(b.end.column*d.characterWidth),a.push("
    "),h=(b.end.row-b.start.row-1)*d.lineHeight;if(h<0)return;j=this.$getTop(b.start.row+1,d),a.push("
    ")},this.drawSingleLineMarker=function(a,b,c,d,e,f){var g=f==="background"?0:this.$padding,h=d.lineHeight;if(f==="background")var i=d.width;else i=Math.round((b.end.column+(e||0)-b.start.column)*d.characterWidth);var j=this.$getTop(b.start.row,d),k=Math.round(g+b.start.column*d.characterWidth);a.push("
    ")}}).call(f.prototype),b.Marker=f}),define("ace/layer/text",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/lang","ace/lib/useragent","ace/lib/event_emitter"],function(a,b,c){"use strict";var d=a("../lib/oop"),e=a("../lib/dom"),f=a("../lib/lang"),g=a("../lib/useragent"),h=a("../lib/event_emitter").EventEmitter,i=function(a){this.element=e.createElement("div"),this.element.className="ace_layer ace_text-layer",a.appendChild(this.element),this.$characterSize=this.$measureSizes()||{width:0,height:0},this.$pollSizeChanges()};(function(){d.implement(this,h),this.EOF_CHAR="¶",this.EOL_CHAR="¬",this.TAB_CHAR="→",this.SPACE_CHAR="·",this.$padding=0,this.setPadding=function(a){this.$padding=a,this.element.style.padding="0 "+a+"px"},this.getLineHeight=function(){return this.$characterSize.height||1},this.getCharacterWidth=function(){return this.$characterSize.width||1},this.checkForSizeChanges=function(){var a=this.$measureSizes();a&&(this.$characterSize.width!==a.width||this.$characterSize.height!==a.height)&&(this.$characterSize=a,this._emit("changeCharacterSize",{data:a}))},this.$pollSizeChanges=function(){var a=this;this.$pollSizeChangesTimer=setInterval(function(){a.checkForSizeChanges()},500)},this.$fontStyles={fontFamily:1,fontSize:1,fontWeight:1,fontStyle:1,lineHeight:1},this.$measureSizes=g.isIE||g.isOldGecko?function(){var a=1e3;if(!this.$measureNode){var b=this.$measureNode=e.createElement("div"),c=b.style;c.width=c.height="auto",c.left=c.top=-a*40+"px",c.visibility="hidden",c.position="fixed",c.overflow="visible",c.whiteSpace="nowrap",b.innerHTML=f.stringRepeat("Xy",a);if(this.element.ownerDocument.body)this.element.ownerDocument.body.appendChild(b);else{var d=this.element.parentNode;while(!e.hasCssClass(d,"ace_editor"))d=d.parentNode;d.appendChild(b)}}if(!this.element.offsetWidth)return null;var c=this.$measureNode.style,g=e.computedStyle(this.element);for(var h in this.$fontStyles)c[h]=g[h];var i={height:this.$measureNode.offsetHeight,width:this.$measureNode.offsetWidth/(a*2)};return i.width==0||i.height==0?null:i}:function(){if(!this.$measureNode){var a=this.$measureNode=e.createElement("div"),b=a.style;b.width=b.height="auto",b.left=b.top="-100px",b.visibility="hidden",b.position="fixed",b.overflow="visible",b.whiteSpace="nowrap",a.innerHTML="X";var c=this.element.parentNode;while(c&&!e.hasCssClass(c,"ace_editor"))c=c.parentNode;if(!c)return this.$measureNode=null;c.appendChild(a)}var d=this.$measureNode.getBoundingClientRect(),f={height:d.height,width:d.width};return f.width==0||f.height==0?null:f},this.setSession=function(a){this.session=a},this.showInvisibles=!1,this.setShowInvisibles=function(a){return this.showInvisibles==a?!1:(this.showInvisibles=a,!0)},this.$tabStrings=[],this.$computeTabString=function(){var a=this.session.getTabSize(),b=this.$tabStrings=[0];for(var c=1;c"+this.TAB_CHAR+(new Array(c)).join(" ")+""):b.push((new Array(c+1)).join(" "))},this.updateLines=function(a,b,c){this.$computeTabString(),(this.config.lastRow!=a.lastRow||this.config.firstRow!=a.firstRow)&&this.scrollLines(a),this.config=a;var d=Math.max(b,a.firstRow),f=Math.min(c,a.lastRow),g=this.element.childNodes,h=0;for(var i=a.firstRow;i0;d--)c.removeChild(c.firstChild);if(b.lastRow>a.lastRow)for(var d=this.session.getFoldedRowCount(a.lastRow+1,b.lastRow);d>0;d--)c.removeChild(c.lastChild);if(a.firstRowb.lastRow){var e=this.$renderLinesFragment(a,b.lastRow+1,a.lastRow);c.appendChild(e)}},this.$renderLinesFragment=function(a,b,c){var d=this.element.ownerDocument.createDocumentFragment(),f=b,g=this.session.getNextFoldLine(f),h=g?g.start.row:Infinity;for(;;){f>h&&(f=g.end.row+1,g=this.session.getNextFoldLine(f,g),h=g?g.start.row:Infinity);if(f>c)break;var i=e.createElement("div"),j=[],k=this.session.getTokens(f,f);k.length==1&&this.$renderLine(j,f,k[0].tokens,!1),i.innerHTML=j.join("");if(this.$useLineGroups())i.className="ace_line_group",d.appendChild(i);else{var l=i.childNodes;while(l.length)d.appendChild(l[0])}f++}return d},this.update=function(a){this.$computeTabString(),this.config=a;var b=[],c=a.firstRow,d=a.lastRow,f=c,g=this.session.getNextFoldLine(f),h=g?g.start.row:Infinity;for(;;){f>h&&(f=g.end.row+1,g=this.session.getNextFoldLine(f,g),h=g?g.start.row:Infinity);if(f>d)break;this.$useLineGroups()&&b.push("
    ");var i=this.session.getTokens(f,f);i.length==1&&this.$renderLine(b,f,i[0].tokens,!1),this.$useLineGroups()&&b.push("
    "),f++}this.element=e.setInnerHtml(this.element,b.join(""))},this.$textToken={text:!0,rparen:!0,lparen:!0},this.$renderToken=function(a,b,c,d){var e=this,f=/\t|&|<|( +)|([\v\f \u00a0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000])|[\u1100-\u115F]|[\u11A3-\u11A7]|[\u11FA-\u11FF]|[\u2329-\u232A]|[\u2E80-\u2E99]|[\u2E9B-\u2EF3]|[\u2F00-\u2FD5]|[\u2FF0-\u2FFB]|[\u3000-\u303E]|[\u3041-\u3096]|[\u3099-\u30FF]|[\u3105-\u312D]|[\u3131-\u318E]|[\u3190-\u31BA]|[\u31C0-\u31E3]|[\u31F0-\u321E]|[\u3220-\u3247]|[\u3250-\u32FE]|[\u3300-\u4DBF]|[\u4E00-\uA48C]|[\uA490-\uA4C6]|[\uA960-\uA97C]|[\uAC00-\uD7A3]|[\uD7B0-\uD7C6]|[\uD7CB-\uD7FB]|[\uF900-\uFAFF]|[\uFE10-\uFE19]|[\uFE30-\uFE52]|[\uFE54-\uFE66]|[\uFE68-\uFE6B]|[\uFF01-\uFF60]|[\uFFE0-\uFFE6]/g,h=function(a,c,d,f,h){if(a.charCodeAt(0)==32)return(new Array(a.length+1)).join(" ");if(a==" "){var i=e.session.getScreenTabSize(b+f);return b+=i-1,e.$tabStrings[i]}if(a=="&")return g.isOldGecko?"&":"&";if(a=="<")return"<";if(a==" "){var j=e.showInvisibles?"ace_cjk ace_invisible":"ace_cjk",k=e.showInvisibles?e.SPACE_CHAR:"";return b+=1,""+k+""}if(a.match(/[\v\f \u00a0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000]/)){if(e.showInvisibles){var k=(new Array(a.length+1)).join(e.SPACE_CHAR);return""+k+""}return" "}return b+=1,""+a+""},i=d.replace(f,h);if(!this.$textToken[c.type]){var j="ace_"+c.type.replace(/\./g," ace_"),k="";c.type=="fold"&&(k=" style='width:"+c.value.length*this.config.characterWidth+"px;' "),a.push("",i,"")}else a.push(i);return b+d.length},this.$renderLineCore=function(a,b,c,d,e){var f=0,g=0,h,i=0,j=this;!d||d.length==0?h=Number.MAX_VALUE:h=d[0],e||a.push("
    ");for(var k=0;k=h)i=j.$renderToken(a,i,l,m.substring(0,h-f)),m=m.substring(h-f),f=h,e||a.push("
    ","
    "),g++,i=0,h=d[g]||Number.MAX_VALUE;m.length!=0&&(f+=m.length,i=j.$renderToken(a,i,l,m))}}this.showInvisibles&&(b!==this.session.getLength()-1?a.push(""+this.EOL_CHAR+""):a.push(""+this.EOF_CHAR+"")),e||a.push("
    ")},this.$renderLine=function(a,b,c,d){if(!this.session.isRowFolded(b)){var e=this.session.getRowSplitData(b);this.$renderLineCore(a,b,c,e,d)}else this.$renderFoldLine(a,b,c,d)},this.$renderFoldLine=function(a,b,c,d){function h(a,b,c){var d=0,e=0;while(e+a[d].value.lengthc-b&&(f=f.substring(0,c-b)),g.push({type:a[d].type,value:f}),e=b+f.length,d+=1}while(ec&&(f=f.substring(0,c-e)),g.push({type:a[d].type,value:f}),e+=f.length,d+=1}}var e=this.session,f=e.getFoldLine(b),g=[];f.walk(function(a,b,d,e,f){a?g.push({type:"fold",value:a}):(f&&(c=this.session.getTokens(b,b)[0].tokens),c.length!=0&&h(c,e,d))}.bind(this),f.end.row,this.session.getLine(f.end.row).length);var i=this.session.$useWrapMode?this.session.$wrapData[b]:null;this.$renderLineCore(a,b,g,i,d)},this.$useLineGroups=function(){return this.session.getUseWrapMode()},this.destroy=function(){clearInterval(this.$pollSizeChangesTimer),this.$measureNode&&this.$measureNode.parentNode.removeChild(this.$measureNode),delete this.$measureNode}}).call(i.prototype),b.Text=i}),define("ace/layer/cursor",["require","exports","module","ace/lib/dom"],function(a,b,c){"use strict";var d=a("../lib/dom"),e=function(a){this.element=d.createElement("div"),this.element.className="ace_layer ace_cursor-layer",a.appendChild(this.element),this.isVisible=!1,this.cursors=[],this.cursor=this.addCursor()};(function(){this.$padding=0,this.setPadding=function(a){this.$padding=a},this.setSession=function(a){this.session=a},this.addCursor=function(){var a=d.createElement("div"),b="ace_cursor";return this.isVisible||(b+=" ace_hidden"),this.overwrite&&(b+=" ace_overwrite"),a.className=b,this.element.appendChild(a),this.cursors.push(a),a},this.removeCursor=function(){if(this.cursors.length>1){var a=this.cursors.pop();return a.parentNode.removeChild(a),a}},this.hideCursor=function(){this.isVisible=!1;for(var a=this.cursors.length;a--;)d.addCssClass(this.cursors[a],"ace_hidden");clearInterval(this.blinkId)},this.showCursor=function(){this.isVisible=!0;for(var a=this.cursors.length;a--;)d.removeCssClass(this.cursors[a],"ace_hidden");this.element.style.visibility="",this.restartTimer()},this.restartTimer=function(){clearInterval(this.blinkId);if(!this.isVisible)return;var a=this.element;this.blinkId=setInterval(function(){a.style.visibility="hidden",setTimeout(function(){a.style.visibility="visible"},400)},1e3)},this.getPixelPosition=function(a,b){if(!this.config||!this.session)return{left:0,top:0};a||(a=this.session.selection.getCursor());var c=this.session.documentToScreenPosition(a),d=Math.round(this.$padding+c.column*this.config.characterWidth),e=(c.row-(b?this.config.firstRowScreen:0))*this.config.lineHeight;return{left:d,top:e}},this.update=function(a){this.config=a;if(this.session.selectionMarkerCount>1){var b=this.session.$selectionMarkers,c=0,d,e=0;for(var c=b.length;c--;){d=b[c];var f=this.getPixelPosition(d.cursor,!0),g=(this.cursors[e++]||this.addCursor()).style;g.left=f.left+"px",g.top=f.top+"px",g.width=a.characterWidth+"px",g.height=a.lineHeight+"px"}if(e>1)while(this.cursors.length>e)this.removeCursor()}else{var f=this.getPixelPosition(null,!0),g=this.cursor.style;g.left=f.left+"px",g.top=f.top+"px",g.width=a.characterWidth+"px",g.height=a.lineHeight+"px";while(this.cursors.length>1)this.removeCursor()}var h=this.session.getOverwrite();h!=this.overwrite&&this.$setOverite(h),this.restartTimer()},this.$setOverite=function(a){this.overwrite=a;for(var b=this.cursors.length;b--;)a?d.addCssClass(this.cursors[b],"ace_overwrite"):d.removeCssClass(this.cursors[b],"ace_overwrite")},this.destroy=function(){clearInterval(this.blinkId)}}).call(e.prototype),b.Cursor=e}),define("ace/scrollbar",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/event","ace/lib/event_emitter"],function(a,b,c){"use strict";var d=a("./lib/oop"),e=a("./lib/dom"),f=a("./lib/event"),g=a("./lib/event_emitter").EventEmitter,h=function(a){this.element=e.createElement("div"),this.element.className="ace_sb",this.inner=e.createElement("div"),this.element.appendChild(this.inner),a.appendChild(this.element),this.width=e.scrollbarWidth(a.ownerDocument),this.element.style.width=(this.width||15)+5+"px",f.addListener(this.element,"scroll",this.onScroll.bind(this))};(function(){d.implement(this,g),this.onScroll=function(){this._emit("scroll",{data:this.element.scrollTop})},this.getWidth=function(){return this.width},this.setHeight=function(a){this.element.style.height=a+"px"},this.setInnerHeight=function(a){this.inner.style.height=a+"px"},this.setScrollTop=function(a){this.element.scrollTop=a}}).call(h.prototype),b.ScrollBar=h}),define("ace/renderloop",["require","exports","module","ace/lib/event"],function(a,b,c){"use strict";var d=a("./lib/event"),e=function(a,b){this.onRender=a,this.pending=!1,this.changes=0,this.window=b||window};(function(){this.schedule=function(a){this.changes=this.changes|a;if(!this.pending){this.pending=!0;var b=this;d.nextTick(function(){b.pending=!1;var a;while(a=b.changes)b.changes=0,b.onRender(a)},this.window)}}}).call(e.prototype),b.RenderLoop=e}),define("text!ace/css/editor.css",[],"@import url(//fonts.googleapis.com/css?family=Droid+Sans+Mono);\n\n.ace_editor {\n position: absolute;\n overflow: hidden;\n font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Droid Sans Mono', 'Consolas', monospace;\n font-size: 12px;\n}\n\n.ace_scroller {\n position: absolute;\n overflow-x: scroll;\n overflow-y: hidden;\n}\n\n.ace_content {\n position: absolute;\n box-sizing: border-box;\n -moz-box-sizing: border-box;\n -webkit-box-sizing: border-box;\n cursor: text;\n}\n\n.ace_composition {\n position: absolute;\n background: #555;\n color: #DDD;\n z-index: 4;\n}\n\n.ace_gutter {\n position: absolute;\n overflow : hidden;\n height: 100%;\n width: auto;\n cursor: default;\n z-index: 1000;\n}\n\n.ace_gutter.horscroll {\n box-shadow: 0px 0px 20px rgba(0,0,0,0.4);\n}\n\n.ace_gutter-cell {\n padding-left: 19px;\n padding-right: 6px;\n}\n\n.ace_gutter-cell.ace_error {\n background-image: url(\"data:image/gif,GIF89a%10%00%10%00%D5%00%00%F5or%F5%87%88%F5nr%F4ns%EBmq%F5z%7F%DDJT%DEKS%DFOW%F1Yc%F2ah%CE(7%CE)8%D18E%DD%40M%F2KZ%EBU%60%F4%60m%DCir%C8%16(%C8%19*%CE%255%F1%3FR%F1%3FS%E6%AB%B5%CA%5DI%CEn%5E%F7%A2%9A%C9G%3E%E0a%5B%F7%89%85%F5yy%F6%82%80%ED%82%80%FF%BF%BF%E3%C4%C4%FF%FF%FF%FF%FF%FF%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00!%F9%04%01%00%00%25%00%2C%00%00%00%00%10%00%10%00%00%06p%C0%92pH%2C%1A%8F%C8%D2H%93%E1d4%23%E4%88%D3%09mB%1DN%B48%F5%90%40%60%92G%5B%94%20%3E%22%D2%87%24%FA%20%24%C5%06A%00%20%B1%07%02B%A38%89X.v%17%82%11%13q%10%0Fi%24%0F%8B%10%7BD%12%0Ei%09%92%09%0EpD%18%15%24%0A%9Ci%05%0C%18F%18%0B%07%04%01%04%06%A0H%18%12%0D%14%0D%12%A1I%B3%B4%B5IA%00%3B\");\n background-repeat: no-repeat;\n background-position: 2px center;\n}\n\n.ace_gutter-cell.ace_warning {\n background-image: url(\"data:image/gif,GIF89a%10%00%10%00%D5%00%00%FF%DBr%FF%DE%81%FF%E2%8D%FF%E2%8F%FF%E4%96%FF%E3%97%FF%E5%9D%FF%E6%9E%FF%EE%C1%FF%C8Z%FF%CDk%FF%D0s%FF%D4%81%FF%D5%82%FF%D5%83%FF%DC%97%FF%DE%9D%FF%E7%B8%FF%CCl%7BQ%13%80U%15%82W%16%81U%16%89%5B%18%87%5B%18%8C%5E%1A%94d%1D%C5%83-%C9%87%2F%C6%84.%C6%85.%CD%8B2%C9%871%CB%8A3%CD%8B5%DC%98%3F%DF%9BB%E0%9CC%E1%A5U%CB%871%CF%8B5%D1%8D6%DB%97%40%DF%9AB%DD%99B%E3%B0p%E7%CC%AE%FF%FF%FF%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00!%F9%04%01%00%00%2F%00%2C%00%00%00%00%10%00%10%00%00%06a%C0%97pH%2C%1A%8FH%A1%ABTr%25%87%2B%04%82%F4%7C%B9X%91%08%CB%99%1C!%26%13%84*iJ9(%15G%CA%84%14%01%1A%97%0C%03%80%3A%9A%3E%81%84%3E%11%08%B1%8B%20%02%12%0F%18%1A%0F%0A%03'F%1C%04%0B%10%16%18%10%0B%05%1CF%1D-%06%07%9A%9A-%1EG%1B%A0%A1%A0U%A4%A5%A6BA%00%3B\");\n background-repeat: no-repeat;\n background-position: 2px center;\n}\n\n.ace_gutter-cell.ace_info {\n background-image: url(\"data:image/gif;base64,R0lGODlhEAAQAMQAAAAAAEFBQVJSUl5eXmRkZGtra39/f4WFhYmJiZGRkaampry8vMPDw8zMzNXV1dzc3OTk5Orq6vDw8P///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABQALAAAAAAQABAAAAUuICWOZGmeaBml5XGwFCQSBGyXRSAwtqQIiRuiwIM5BoYVbEFIyGCQoeJGrVptIQA7\");\n background-repeat: no-repeat;\n background-position: 2px center;\n}\n\n.ace_editor .ace_sb {\n position: absolute;\n overflow-x: hidden;\n overflow-y: scroll;\n right: 0;\n}\n\n.ace_editor .ace_sb div {\n position: absolute;\n width: 1px;\n left: 0;\n}\n\n.ace_editor .ace_print_margin_layer {\n z-index: 0;\n position: absolute;\n overflow: hidden;\n margin: 0;\n left: 0;\n height: 100%;\n width: 100%;\n}\n\n.ace_editor .ace_print_margin {\n position: absolute;\n height: 100%;\n}\n\n.ace_editor textarea {\n position: fixed;\n z-index: 0;\n width: 10px;\n height: 30px;\n opacity: 0;\n background: transparent;\n appearance: none;\n -moz-appearance: none;\n border: none;\n resize: none;\n outline: none;\n overflow: hidden;\n}\n\n.ace_layer {\n z-index: 1;\n position: absolute;\n overflow: hidden;\n white-space: nowrap;\n height: 100%;\n width: 100%;\n box-sizing: border-box;\n -moz-box-sizing: border-box;\n -webkit-box-sizing: border-box;\n /* setting pointer-events: auto; on node under the mouse, which changes\n during scroll, will break mouse wheel scrolling in Safari */\n pointer-events: none;\n}\n\n.ace_gutter .ace_layer {\n position: relative;\n min-width: 40px;\n text-align: right;\n pointer-events: auto;\n}\n\n.ace_text-layer {\n color: black;\n}\n\n.ace_cjk {\n display: inline-block;\n text-align: center;\n}\n\n.ace_cursor-layer {\n z-index: 4;\n}\n\n.ace_cursor {\n z-index: 4;\n position: absolute;\n}\n\n.ace_cursor.ace_hidden {\n opacity: 0.2;\n}\n\n.ace_line {\n white-space: nowrap;\n}\n\n.ace_marker-layer .ace_step {\n position: absolute;\n z-index: 3;\n}\n\n.ace_marker-layer .ace_selection {\n position: absolute;\n z-index: 5;\n}\n\n.ace_marker-layer .ace_bracket {\n position: absolute;\n z-index: 6;\n}\n\n.ace_marker-layer .ace_active_line {\n position: absolute;\n z-index: 2;\n}\n\n.ace_gutter .ace_gutter_active_line{\n background-color : #dcdcdc;\n}\n\n.ace_marker-layer .ace_selected_word {\n position: absolute;\n z-index: 4;\n box-sizing: border-box;\n -moz-box-sizing: border-box;\n -webkit-box-sizing: border-box;\n}\n\n.ace_line .ace_fold {\n box-sizing: border-box;\n -moz-box-sizing: border-box;\n -webkit-box-sizing: border-box;\n \n display: inline-block;\n height: 11px;\n margin-top: -2px;\n vertical-align: middle;\n\n background-image: \n url(\"data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%11%00%00%00%09%08%06%00%00%00%D4%E8%C7%0C%00%00%03%1EiCCPICC%20Profile%00%00x%01%85T%DFk%D3P%14%FE%DAe%9D%B0%E1%8B%3Ag%11%09%3Eh%91ndStC%9C%B6kW%BA%CDZ%EA6%B7!H%9B%A6m%5C%9A%C6%24%ED~%B0%07%D9%8Bo%3A%C5w%F1%07%3E%F9%07%0C%D9%83o%7B%92%0D%C6%14a%F8%AC%88%22L%F6%22%B3%9E%9B4M'S%03%B9%F7%BB%DF%F9%EE9'%E7%E4%5E%A0%F9qZ%D3%14%2F%0F%14USO%C5%C2%FC%C4%E4%14%DF%F2%01%5E%1CC%2B%FChM%8B%86%16J%26G%40%0F%D3%B2y%EF%B3%F3%0E%1E%C6lt%EEo%DF%AB%FEc%D5%9A%95%0C%11%F0%1C%20%BE%945%C4%22%E1Y%A0i%5C%D4t%13%E0%D6%89%EF%9D15%C2%CDLsX%A7%04%09%1Fg8oc%81%E1%8C%8D%23%96f45%40%9A%09%C2%07%C5B%3AK%B8%408%98i%E0%F3%0D%D8%CE%81%14%E4'%26%A9%92.%8B%3C%ABER%2F%E5dE%B2%0C%F6%F0%1Fs%83%F2_%B0%A8%94%E9%9B%AD%E7%10%8Dm%9A%19N%D1%7C%8A%DE%1F9%7Dp%8C%E6%00%D5%C1%3F_%18%BDA%B8%9DpX6%E3%A35~B%CD%24%AE%11%26%BD%E7%EEti%98%EDe%9A%97Y)%12%25%1C%24%BCbT%AE3li%E6%0B%03%89%9A%E6%D3%ED%F4P%92%B0%9F4%BF43Y%F3%E3%EDP%95%04%EB1%C5%F5%F6KF%F4%BA%BD%D7%DB%91%93%07%E35%3E%A7)%D6%7F%40%FE%BD%F7%F5r%8A%E5y%92%F0%EB%B4%1E%8D%D5%F4%5B%92%3AV%DB%DB%E4%CD%A6%23%C3%C4wQ%3F%03HB%82%8E%1Cd(%E0%91B%0Ca%9Ac%C4%AA%F8L%16%19%22J%A4%D2itTy%B28%D6%3B(%93%96%ED%1CGx%C9_%0E%B8%5E%16%F5%5B%B2%B8%F6%E0%FB%9E%DD%25%D7%8E%BC%15%85%C5%B7%A3%D8Q%ED%B5%81%E9%BA%B2%13%9A%1B%7Fua%A5%A3n%E17%B9%E5%9B%1Bm%AB%0B%08Q%FE%8A%E5%B1H%5Ee%CAO%82Q%D7u6%E6%90S%97%FCu%0B%CF2%94%EE%25v%12X%0C%BA%AC%F0%5E%F8*l%0AO%85%17%C2%97%BF%D4%C8%CE%DE%AD%11%CB%80q%2C%3E%AB%9ES%CD%C6%EC%25%D2L%D2%EBd%B8%BF%8A%F5B%C6%18%F9%901CZ%9D%BE%24M%9C%8A9%F2%DAP%0B'%06w%82%EB%E6%E2%5C%2F%D7%07%9E%BB%CC%5D%E1%FA%B9%08%AD.r%23%8E%C2%17%F5E%7C!%F0%BE3%BE%3E_%B7o%88a%A7%DB%BE%D3d%EB%A31Z%EB%BB%D3%91%BA%A2%B1z%94%8F%DB'%F6%3D%8E%AA%13%19%B2%B1%BE%B1~V%08%2B%B4%A2cjJ%B3tO%00%03%25mN%97%F3%05%93%EF%11%84%0B%7C%88%AE-%89%8F%ABbW%90O%2B%0Ao%99%0C%5E%97%0CI%AFH%D9.%B0%3B%8F%ED%03%B6S%D6%5D%E6i_s9%F3*p%E9%1B%FD%C3%EB.7U%06%5E%19%C0%D1s.%17%A03u%E4%09%B0%7C%5E%2C%EB%15%DB%1F%3C%9E%B7%80%91%3B%DBc%AD%3Dma%BA%8B%3EV%AB%DBt.%5B%1E%01%BB%0F%AB%D5%9F%CF%AA%D5%DD%E7%E4%7F%0Bx%A3%FC%06%A9%23%0A%D6%C2%A1_2%00%00%00%09pHYs%00%00%0B%13%00%00%0B%13%01%00%9A%9C%18%00%00%00%B5IDAT(%15%A5%91%3D%0E%02!%10%85ac%E1%05%D6%CE%D6%C6%CE%D2%E8%ED%CD%DE%C0%C6%D6N.%E0V%F8%3D%9Ca%891XH%C2%BE%D9y%3F%90!%E6%9C%C3%BFk%E5%011%C6-%F5%C8N%04%DF%BD%FF%89%DFt%83DN%60%3E%F3%AB%A0%DE%1A%5Dg%BE%10Q%97%1B%40%9C%A8o%10%8F%5E%828%B4%1B%60%87%F6%02%26%85%1Ch%1E%C1%2B%5Bk%FF%86%EE%B7j%09%9A%DA%9B%ACe%A3%F9%EC%DA!9%B4%D5%A6%81%86%86%98%CC%3C%5B%40%FA%81%B3%E9%CB%23%94%C16Azo%05%D4%E1%C1%95a%3B%8A'%A0%E8%CC%17%22%85%1D%BA%00%A2%FA%DC%0A%94%D1%D1%8D%8B%3A%84%17B%C7%60%1A%25Z%FC%8D%00%00%00%00IEND%AEB%60%82\"),\n url(\"data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%05%00%00%007%08%06%00%00%00%C4%DD%80C%00%00%03%1EiCCPICC%20Profile%00%00x%01%85T%DFk%D3P%14%FE%DAe%9D%B0%E1%8B%3Ag%11%09%3Eh%91ndStC%9C%B6kW%BA%CDZ%EA6%B7!H%9B%A6m%5C%9A%C6%24%ED~%B0%07%D9%8Bo%3A%C5w%F1%07%3E%F9%07%0C%D9%83o%7B%92%0D%C6%14a%F8%AC%88%22L%F6%22%B3%9E%9B4M'S%03%B9%F7%BB%DF%F9%EE9'%E7%E4%5E%A0%F9qZ%D3%14%2F%0F%14USO%C5%C2%FC%C4%E4%14%DF%F2%01%5E%1CC%2B%FChM%8B%86%16J%26G%40%0F%D3%B2y%EF%B3%F3%0E%1E%C6lt%EEo%DF%AB%FEc%D5%9A%95%0C%11%F0%1C%20%BE%945%C4%22%E1Y%A0i%5C%D4t%13%E0%D6%89%EF%9D15%C2%CDLsX%A7%04%09%1Fg8oc%81%E1%8C%8D%23%96f45%40%9A%09%C2%07%C5B%3AK%B8%408%98i%E0%F3%0D%D8%CE%81%14%E4'%26%A9%92.%8B%3C%ABER%2F%E5dE%B2%0C%F6%F0%1Fs%83%F2_%B0%A8%94%E9%9B%AD%E7%10%8Dm%9A%19N%D1%7C%8A%DE%1F9%7Dp%8C%E6%00%D5%C1%3F_%18%BDA%B8%9DpX6%E3%A35~B%CD%24%AE%11%26%BD%E7%EEti%98%EDe%9A%97Y)%12%25%1C%24%BCbT%AE3li%E6%0B%03%89%9A%E6%D3%ED%F4P%92%B0%9F4%BF43Y%F3%E3%EDP%95%04%EB1%C5%F5%F6KF%F4%BA%BD%D7%DB%91%93%07%E35%3E%A7)%D6%7F%40%FE%BD%F7%F5r%8A%E5y%92%F0%EB%B4%1E%8D%D5%F4%5B%92%3AV%DB%DB%E4%CD%A6%23%C3%C4wQ%3F%03HB%82%8E%1Cd(%E0%91B%0Ca%9Ac%C4%AA%F8L%16%19%22J%A4%D2itTy%B28%D6%3B(%93%96%ED%1CGx%C9_%0E%B8%5E%16%F5%5B%B2%B8%F6%E0%FB%9E%DD%25%D7%8E%BC%15%85%C5%B7%A3%D8Q%ED%B5%81%E9%BA%B2%13%9A%1B%7Fua%A5%A3n%E17%B9%E5%9B%1Bm%AB%0B%08Q%FE%8A%E5%B1H%5Ee%CAO%82Q%D7u6%E6%90S%97%FCu%0B%CF2%94%EE%25v%12X%0C%BA%AC%F0%5E%F8*l%0AO%85%17%C2%97%BF%D4%C8%CE%DE%AD%11%CB%80q%2C%3E%AB%9ES%CD%C6%EC%25%D2L%D2%EBd%B8%BF%8A%F5B%C6%18%F9%901CZ%9D%BE%24M%9C%8A9%F2%DAP%0B'%06w%82%EB%E6%E2%5C%2F%D7%07%9E%BB%CC%5D%E1%FA%B9%08%AD.r%23%8E%C2%17%F5E%7C!%F0%BE3%BE%3E_%B7o%88a%A7%DB%BE%D3d%EB%A31Z%EB%BB%D3%91%BA%A2%B1z%94%8F%DB'%F6%3D%8E%AA%13%19%B2%B1%BE%B1~V%08%2B%B4%A2cjJ%B3tO%00%03%25mN%97%F3%05%93%EF%11%84%0B%7C%88%AE-%89%8F%ABbW%90O%2B%0Ao%99%0C%5E%97%0CI%AFH%D9.%B0%3B%8F%ED%03%B6S%D6%5D%E6i_s9%F3*p%E9%1B%FD%C3%EB.7U%06%5E%19%C0%D1s.%17%A03u%E4%09%B0%7C%5E%2C%EB%15%DB%1F%3C%9E%B7%80%91%3B%DBc%AD%3Dma%BA%8B%3EV%AB%DBt.%5B%1E%01%BB%0F%AB%D5%9F%CF%AA%D5%DD%E7%E4%7F%0Bx%A3%FC%06%A9%23%0A%D6%C2%A1_2%00%00%00%09pHYs%00%00%0B%13%00%00%0B%13%01%00%9A%9C%18%00%00%00%3AIDAT8%11c%FC%FF%FF%7F%18%03%1A%60%01%F2%3F%A0%891%80%04%FF%11-%F8%17%9BJ%E2%05%B1ZD%81v%26t%E7%80%F8%A3%82h%A12%1A%20%A3%01%02%0F%01%BA%25%06%00%19%C0%0D%AEF%D5%3ES%00%00%00%00IEND%AEB%60%82\");\n background-repeat: no-repeat, repeat-x;\n background-position: center center, top left;\n color: transparent;\n\n border: 1px solid black;\n -moz-border-radius: 2px;\n -webkit-border-radius: 2px;\n border-radius: 2px;\n \n cursor: pointer;\n pointer-events: auto;\n}\n\n.ace_dark .ace_fold {\n}\n\n.ace_fold:hover{\n background-image: \n url(\"data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%11%00%00%00%09%08%06%00%00%00%D4%E8%C7%0C%00%00%03%1EiCCPICC%20Profile%00%00x%01%85T%DFk%D3P%14%FE%DAe%9D%B0%E1%8B%3Ag%11%09%3Eh%91ndStC%9C%B6kW%BA%CDZ%EA6%B7!H%9B%A6m%5C%9A%C6%24%ED~%B0%07%D9%8Bo%3A%C5w%F1%07%3E%F9%07%0C%D9%83o%7B%92%0D%C6%14a%F8%AC%88%22L%F6%22%B3%9E%9B4M'S%03%B9%F7%BB%DF%F9%EE9'%E7%E4%5E%A0%F9qZ%D3%14%2F%0F%14USO%C5%C2%FC%C4%E4%14%DF%F2%01%5E%1CC%2B%FChM%8B%86%16J%26G%40%0F%D3%B2y%EF%B3%F3%0E%1E%C6lt%EEo%DF%AB%FEc%D5%9A%95%0C%11%F0%1C%20%BE%945%C4%22%E1Y%A0i%5C%D4t%13%E0%D6%89%EF%9D15%C2%CDLsX%A7%04%09%1Fg8oc%81%E1%8C%8D%23%96f45%40%9A%09%C2%07%C5B%3AK%B8%408%98i%E0%F3%0D%D8%CE%81%14%E4'%26%A9%92.%8B%3C%ABER%2F%E5dE%B2%0C%F6%F0%1Fs%83%F2_%B0%A8%94%E9%9B%AD%E7%10%8Dm%9A%19N%D1%7C%8A%DE%1F9%7Dp%8C%E6%00%D5%C1%3F_%18%BDA%B8%9DpX6%E3%A35~B%CD%24%AE%11%26%BD%E7%EEti%98%EDe%9A%97Y)%12%25%1C%24%BCbT%AE3li%E6%0B%03%89%9A%E6%D3%ED%F4P%92%B0%9F4%BF43Y%F3%E3%EDP%95%04%EB1%C5%F5%F6KF%F4%BA%BD%D7%DB%91%93%07%E35%3E%A7)%D6%7F%40%FE%BD%F7%F5r%8A%E5y%92%F0%EB%B4%1E%8D%D5%F4%5B%92%3AV%DB%DB%E4%CD%A6%23%C3%C4wQ%3F%03HB%82%8E%1Cd(%E0%91B%0Ca%9Ac%C4%AA%F8L%16%19%22J%A4%D2itTy%B28%D6%3B(%93%96%ED%1CGx%C9_%0E%B8%5E%16%F5%5B%B2%B8%F6%E0%FB%9E%DD%25%D7%8E%BC%15%85%C5%B7%A3%D8Q%ED%B5%81%E9%BA%B2%13%9A%1B%7Fua%A5%A3n%E17%B9%E5%9B%1Bm%AB%0B%08Q%FE%8A%E5%B1H%5Ee%CAO%82Q%D7u6%E6%90S%97%FCu%0B%CF2%94%EE%25v%12X%0C%BA%AC%F0%5E%F8*l%0AO%85%17%C2%97%BF%D4%C8%CE%DE%AD%11%CB%80q%2C%3E%AB%9ES%CD%C6%EC%25%D2L%D2%EBd%B8%BF%8A%F5B%C6%18%F9%901CZ%9D%BE%24M%9C%8A9%F2%DAP%0B'%06w%82%EB%E6%E2%5C%2F%D7%07%9E%BB%CC%5D%E1%FA%B9%08%AD.r%23%8E%C2%17%F5E%7C!%F0%BE3%BE%3E_%B7o%88a%A7%DB%BE%D3d%EB%A31Z%EB%BB%D3%91%BA%A2%B1z%94%8F%DB'%F6%3D%8E%AA%13%19%B2%B1%BE%B1~V%08%2B%B4%A2cjJ%B3tO%00%03%25mN%97%F3%05%93%EF%11%84%0B%7C%88%AE-%89%8F%ABbW%90O%2B%0Ao%99%0C%5E%97%0CI%AFH%D9.%B0%3B%8F%ED%03%B6S%D6%5D%E6i_s9%F3*p%E9%1B%FD%C3%EB.7U%06%5E%19%C0%D1s.%17%A03u%E4%09%B0%7C%5E%2C%EB%15%DB%1F%3C%9E%B7%80%91%3B%DBc%AD%3Dma%BA%8B%3EV%AB%DBt.%5B%1E%01%BB%0F%AB%D5%9F%CF%AA%D5%DD%E7%E4%7F%0Bx%A3%FC%06%A9%23%0A%D6%C2%A1_2%00%00%00%09pHYs%00%00%0B%13%00%00%0B%13%01%00%9A%9C%18%00%00%00%B5IDAT(%15%A5%91%3D%0E%02!%10%85ac%E1%05%D6%CE%D6%C6%CE%D2%E8%ED%CD%DE%C0%C6%D6N.%E0V%F8%3D%9Ca%891XH%C2%BE%D9y%3F%90!%E6%9C%C3%BFk%E5%011%C6-%F5%C8N%04%DF%BD%FF%89%DFt%83DN%60%3E%F3%AB%A0%DE%1A%5Dg%BE%10Q%97%1B%40%9C%A8o%10%8F%5E%828%B4%1B%60%87%F6%02%26%85%1Ch%1E%C1%2B%5Bk%FF%86%EE%B7j%09%9A%DA%9B%ACe%A3%F9%EC%DA!9%B4%D5%A6%81%86%86%98%CC%3C%5B%40%FA%81%B3%E9%CB%23%94%C16Azo%05%D4%E1%C1%95a%3B%8A'%A0%E8%CC%17%22%85%1D%BA%00%A2%FA%DC%0A%94%D1%D1%8D%8B%3A%84%17B%C7%60%1A%25Z%FC%8D%00%00%00%00IEND%AEB%60%82\"),\n url(\"data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%05%00%00%007%08%06%00%00%00%C4%DD%80C%00%00%03%1EiCCPICC%20Profile%00%00x%01%85T%DFk%D3P%14%FE%DAe%9D%B0%E1%8B%3Ag%11%09%3Eh%91ndStC%9C%B6kW%BA%CDZ%EA6%B7!H%9B%A6m%5C%9A%C6%24%ED~%B0%07%D9%8Bo%3A%C5w%F1%07%3E%F9%07%0C%D9%83o%7B%92%0D%C6%14a%F8%AC%88%22L%F6%22%B3%9E%9B4M'S%03%B9%F7%BB%DF%F9%EE9'%E7%E4%5E%A0%F9qZ%D3%14%2F%0F%14USO%C5%C2%FC%C4%E4%14%DF%F2%01%5E%1CC%2B%FChM%8B%86%16J%26G%40%0F%D3%B2y%EF%B3%F3%0E%1E%C6lt%EEo%DF%AB%FEc%D5%9A%95%0C%11%F0%1C%20%BE%945%C4%22%E1Y%A0i%5C%D4t%13%E0%D6%89%EF%9D15%C2%CDLsX%A7%04%09%1Fg8oc%81%E1%8C%8D%23%96f45%40%9A%09%C2%07%C5B%3AK%B8%408%98i%E0%F3%0D%D8%CE%81%14%E4'%26%A9%92.%8B%3C%ABER%2F%E5dE%B2%0C%F6%F0%1Fs%83%F2_%B0%A8%94%E9%9B%AD%E7%10%8Dm%9A%19N%D1%7C%8A%DE%1F9%7Dp%8C%E6%00%D5%C1%3F_%18%BDA%B8%9DpX6%E3%A35~B%CD%24%AE%11%26%BD%E7%EEti%98%EDe%9A%97Y)%12%25%1C%24%BCbT%AE3li%E6%0B%03%89%9A%E6%D3%ED%F4P%92%B0%9F4%BF43Y%F3%E3%EDP%95%04%EB1%C5%F5%F6KF%F4%BA%BD%D7%DB%91%93%07%E35%3E%A7)%D6%7F%40%FE%BD%F7%F5r%8A%E5y%92%F0%EB%B4%1E%8D%D5%F4%5B%92%3AV%DB%DB%E4%CD%A6%23%C3%C4wQ%3F%03HB%82%8E%1Cd(%E0%91B%0Ca%9Ac%C4%AA%F8L%16%19%22J%A4%D2itTy%B28%D6%3B(%93%96%ED%1CGx%C9_%0E%B8%5E%16%F5%5B%B2%B8%F6%E0%FB%9E%DD%25%D7%8E%BC%15%85%C5%B7%A3%D8Q%ED%B5%81%E9%BA%B2%13%9A%1B%7Fua%A5%A3n%E17%B9%E5%9B%1Bm%AB%0B%08Q%FE%8A%E5%B1H%5Ee%CAO%82Q%D7u6%E6%90S%97%FCu%0B%CF2%94%EE%25v%12X%0C%BA%AC%F0%5E%F8*l%0AO%85%17%C2%97%BF%D4%C8%CE%DE%AD%11%CB%80q%2C%3E%AB%9ES%CD%C6%EC%25%D2L%D2%EBd%B8%BF%8A%F5B%C6%18%F9%901CZ%9D%BE%24M%9C%8A9%F2%DAP%0B'%06w%82%EB%E6%E2%5C%2F%D7%07%9E%BB%CC%5D%E1%FA%B9%08%AD.r%23%8E%C2%17%F5E%7C!%F0%BE3%BE%3E_%B7o%88a%A7%DB%BE%D3d%EB%A31Z%EB%BB%D3%91%BA%A2%B1z%94%8F%DB'%F6%3D%8E%AA%13%19%B2%B1%BE%B1~V%08%2B%B4%A2cjJ%B3tO%00%03%25mN%97%F3%05%93%EF%11%84%0B%7C%88%AE-%89%8F%ABbW%90O%2B%0Ao%99%0C%5E%97%0CI%AFH%D9.%B0%3B%8F%ED%03%B6S%D6%5D%E6i_s9%F3*p%E9%1B%FD%C3%EB.7U%06%5E%19%C0%D1s.%17%A03u%E4%09%B0%7C%5E%2C%EB%15%DB%1F%3C%9E%B7%80%91%3B%DBc%AD%3Dma%BA%8B%3EV%AB%DBt.%5B%1E%01%BB%0F%AB%D5%9F%CF%AA%D5%DD%E7%E4%7F%0Bx%A3%FC%06%A9%23%0A%D6%C2%A1_2%00%00%00%09pHYs%00%00%0B%13%00%00%0B%13%01%00%9A%9C%18%00%00%003IDAT8%11c%FC%FF%FF%7F%3E%03%1A%60%01%F2%3F%A3%891%80%04%FFQ%26%F8w%C0%B43%A1%DB%0C%E2%8F%0A%A2%85%CAh%80%8C%06%08%3C%04%E8%96%18%00%A3S%0D%CD%CF%D8%C1%9D%00%00%00%00IEND%AEB%60%82\");\n background-repeat: no-repeat, repeat-x;\n background-position: center center, top left;\n}\n\n.ace_dragging .ace_content {\n cursor: move;\n}\n\n.ace_folding-enabled > .ace_gutter-cell {\n padding-right: 13px;\n}\n\n.ace_fold-widget {\n box-sizing: border-box;\n -moz-box-sizing: border-box;\n -webkit-box-sizing: border-box;\n\n margin: 0 -12px 1px 1px;\n display: inline-block;\n height: 14px;\n width: 11px;\n vertical-align: text-bottom;\n \n background-image: url(\"data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%05%00%00%00%05%08%06%00%00%00%8Do%26%E5%00%00%004IDATx%DAe%8A%B1%0D%000%0C%C2%F2%2CK%96%BC%D0%8F9%81%88H%E9%D0%0E%96%C0%10%92%3E%02%80%5E%82%E4%A9*-%EEsw%C8%CC%11%EE%96w%D8%DC%E9*Eh%0C%151(%00%00%00%00IEND%AEB%60%82\");\n background-repeat: no-repeat;\n background-position: center 5px;\n\n border-radius: 3px;\n}\n\n.ace_fold-widget.end {\n background-image: url(\"data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%05%00%00%00%05%08%06%00%00%00%8Do%26%E5%00%00%004IDATx%DAm%C7%C1%09%000%08C%D1%8C%ECE%C8E(%8E%EC%02)%1EZJ%F1%C1'%04%07I%E1%E5%EE%CAL%F5%A2%99%99%22%E2%D6%1FU%B5%FE0%D9x%A7%26Wz5%0E%D5%00%00%00%00IEND%AEB%60%82\");\n}\n\n.ace_fold-widget.closed {\n background-image: url(\"data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%03%00%00%00%06%08%06%00%00%00%06%E5%24%0C%00%00%009IDATx%DA5%CA%C1%09%000%08%03%C0%AC*(%3E%04%C1%0D%BA%B1%23%A4Uh%E0%20%81%C0%CC%F8%82%81%AA%A2%AArGfr%88%08%11%11%1C%DD%7D%E0%EE%5B%F6%F6%CB%B8%05Q%2F%E9tai%D9%00%00%00%00IEND%AEB%60%82\");\n}\n\n.ace_fold-widget:hover {\n border: 1px solid rgba(0, 0, 0, 0.3);\n background-color: rgba(255, 255, 255, 0.2);\n -moz-box-shadow:inset 0 1px 1px rgba(255, 255, 255, 0.7);\n -moz-box-shadow: 0 1px 1px rgba(255, 255, 255, 0.7);\n -webkit-box-shadow:inset 0 1px 1px rgba(255, 255, 255, 0.7);\n -webkit-box-shadow: 0 1px 1px rgba(255, 255, 255, 0.7);\n box-shadow:inset 0 1px 1px rgba(255, 255, 255, 0.7);\n box-shadow: 0 1px 1px rgba(255, 255, 255, 0.7);\n background-position: center 4px;\n}\n\n.ace_fold-widget:active {\n border: 1px solid rgba(0, 0, 0, 0.4);\n background-color: rgba(0, 0, 0, 0.05);\n -moz-box-shadow:inset 0 1px 1px rgba(255, 255, 255);\n -moz-box-shadow: 0 1px 1px rgba(255, 255, 255, 0.8);\n -webkit-box-shadow:inset 0 1px 1px rgba(255, 255, 255);\n -webkit-box-shadow: 0 1px 1px rgba(255, 255, 255, 0.8);\n box-shadow:inset 0 1px 1px rgba(255, 255, 255);\n box-shadow: 0 1px 1px rgba(255, 255, 255, 0.8);\n}\n\n.ace_fold-widget.invalid {\n background-color: #FFB4B4;\n border-color: #DE5555;\n}\n"),define("ace/multi_select",["require","exports","module","ace/range_list","ace/range","ace/selection","ace/mouse/multi_select_handler","ace/commands/multi_select_commands","ace/search","ace/edit_session","ace/editor"],function(a,b,c){function j(a,b,c){return i.$options.wrap=!0,i.$options.needle=b,i.$options.backwards=c==-1,i.find(a)}function m(a,b){return a.row==b.row&&a.column==b.column}function n(a){a.$onAddRange=a.$onAddRange.bind(a),a.$onRemoveRange=a.$onRemoveRange.bind(a),a.$onMultiSelect=a.$onMultiSelect.bind(a),a.$onSingleSelect=a.$onSingleSelect.bind(a),b.onSessionChange.call(a,a),a.on("changeSession",b.onSessionChange.bind(a)),a.on("mousedown",g),a.commands.addCommands(b.commands.defaultCommands)}var d=a("./range_list").RangeList,e=a("./range").Range,f=a("./selection").Selection,g=a("./mouse/multi_select_handler").onMouseDown;b.commands=a("./commands/multi_select_commands");var h=a("./search").Search,i=new h,k=a("./edit_session").EditSession;(function(){this.getSelectionMarkers=function(){return this.$selectionMarkers}}).call(k.prototype),function(){this.ranges=null,this.rangeList=null,this.addRange=function(a){if(!this.inMultiSelectMode&&this.rangeCount==0){var b=this.toOrientedRange();if(!a||!a.isEqual(b))this.rangeList.add(b),this.$onAddRange(b)}if(!a)return;a.cursor||(a.cursor=a.end);var c=this.rangeList.add(a);this.$onAddRange(a),c.length&&this.$onRemoveRange(c),this.rangeCount>0&&!this.inMultiSelectMode&&(this._emit("multiSelect"),this.inMultiSelectMode=!0,this.session.$undoSelect=!1,this.rangeList.attach(this.session))},this.toSingleRange=function(a){a=a||this.ranges[0];var b=this.rangeList.removeAll();b.length&&this.$onRemoveRange(b),a&&this.fromOrientedRange(a)},this.substractPoint=function(a){var b=this.rangeList.substractPoint(a);if(b)return this.$onRemoveRange(b),b[0]},this.mergeOverlappingRanges=function(){var a=this.rangeList.merge();a.length?this.$onRemoveRange(a):this.ranges[0]&&this.fromOrientedRange(this.ranges[0])},this.$onAddRange=function(a){this.rangeCount=this.rangeList.ranges.length,this.ranges.unshift(a),this.fromOrientedRange(a),this._emit("addRange",{range:a})},this.$onRemoveRange=function(a){this.rangeCount=this.rangeList.ranges.length;if(this.rangeCount==1&&this.inMultiSelectMode){var b=this.rangeList.ranges.pop();a.push(b),this.rangeCount=0}for(var c=a.length;c--;){var d=this.ranges.indexOf(a[c]);this.ranges.splice(d,1)}this._emit("removeRange",{ranges:a}),this.rangeCount==0&&this.inMultiSelectMode&&(this.inMultiSelectMode=!1,this._emit("singleSelect"),this.session.$undoSelect=!0,this.rangeList.detach(this.session)),b=b||this.ranges[0],b&&!b.isEqual(this.getRange())&&this.fromOrientedRange(b)},this.$initRangeList=function(){if(this.rangeList)return;this.rangeList=new d,this.ranges=[],this.rangeCount=0},this.getAllRanges=function(){return this.rangeList.ranges.concat()},this.splitIntoLines=function(){if(this.rangeCount>1){var a=this.rangeList.ranges,b=a[a.length-1],c=e.fromPoints(a[0].start,b.end);this.toSingleRange(),this.setSelectionRange(c,b.cursor==b.start)}else{var d=this.session.documentToScreenPosition(this.selectionLead),f=this.session.documentToScreenPosition(this.selectionAnchor),g=this.rectangularRangeBlock(d,f);g.forEach(this.addRange,this)}},this.rectangularRangeBlock=function(a,b,c){var d=[],f=a.column0)p--;if(p>0){var q=0;while(d[q].isEmpty())q++}for(var r=p;r>=q;r--)d[r].isEmpty()&&d.splice(r,1)}return d}}.call(f.prototype);var l=a("./editor").Editor;(function(){this.updateSelectionMarkers=function(){this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.addSelectionMarker=function(a){a.cursor||(a.cursor=a.end);var b=this.getSelectionStyle();return a.marker=this.session.addMarker(a,"ace_selection",b),this.session.$selectionMarkers.push(a),this.session.selectionMarkerCount=this.session.$selectionMarkers.length,a},this.removeSelectionMarkers=function(a){for(var b=a.length;b--;){var c=a[b];if(!c.marker)continue;this.session.removeMarker(c.marker);var d=this.session.$selectionMarkers.indexOf(c);d!=-1&&this.session.$selectionMarkers.splice(d,1)}this.session.selectionMarkerCount=this.session.$selectionMarkers.length},this.$onAddRange=function(a){this.addSelectionMarker(a.range),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onRemoveRange=function(a){this.removeSelectionMarkers(a.ranges),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onMultiSelect=function(a){if(this.inMultiSelectMode)return;this.inMultiSelectMode=!0,this.setStyle("multiselect"),this.keyBinding.addKeyboardHandler(b.commands.keyboardHandler),this.commands.on("exec",this.$onMultiSelectExec),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onSingleSelect=function(a){if(this.session.multiSelect.inVirtualMode)return;this.inMultiSelectMode=!1,this.unsetStyle("multiselect"),this.keyBinding.removeKeyboardHandler(b.commands.keyboardHandler),this.commands.removeEventListener("exec",this.$onMultiSelectExec),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onMultiSelectExec=function(a){var b=a.command,c=a.editor;b.multiSelectAction?b.multiSelectAction=="forEach"?c.forEachSelection(b,a.args):b.multiSelectAction=="single"?(c.exitMultiSelectMode(),b.exec(c,a.args||{})):b.multiSelectAction(c,a.args||{}):(b.exec(c,a.args||{}),c.multiSelect.addRange(c.multiSelect.toOrientedRange()),c.multiSelect.mergeOverlappingRanges()),a.preventDefault()},this.forEachSelection=function(a,b){if(this.inVirtualSelectionMode)return;var c=this.session,d=this.selection,e=d.rangeList,g=d._eventRegistry;d._eventRegistry={};var h=new f(c);this.inVirtualSelectionMode=!0;for(var i=e.ranges.length;i--;)h.fromOrientedRange(e.ranges[i]),this.selection=c.selection=h,a.exec(this,b||{}),h.toOrientedRange(e.ranges[i]);h.detach(),this.selection=c.selection=d,this.inVirtualSelectionMode=!1,d._eventRegistry=g,d.mergeOverlappingRanges(),this.onCursorChange(),this.onSelectionChange()},this.exitMultiSelectMode=function(){if(this.inVirtualSelectionMode)return;this.multiSelect.toSingleRange()},this.getCopyText=function(){var a="";if(this.inMultiSelectMode){var b=this.multiSelect.rangeList.ranges;a=[];for(var c=0;c0)continue;return f==0?d:(f=this.comparePoints(a,e.start),f>=0?d:-d-1)}return-d-1},this.add=function(a){var b=this.pointIndex(a.start);b<0&&(b=-b-1);var c=this.pointIndex(a.end,b);return c<0?c=-c-1:c++,this.ranges.splice(b,c-b,a)},this.addList=function(a){var b=[];for(var c=a.length;c--;)b.push.call(b,this.add(a[c]));return b},this.substractPoint=function(a){var b=this.pointIndex(a);if(b>=0)return this.ranges.splice(b,1)},this.merge=function(){var a=[],b=this.ranges,c=b[0],d;for(var e=1;e=0},this.containsPoint=function(a){return this.pointIndex(a)>=0},this.rangeAtPoint=function(a){var b=this.pointIndex(a);if(b>=0)return this.ranges[b]},this.clipRows=function(a,b){var c=this.ranges;if(c[0].start.row>b||c[c.length-1].start.rowe)break;l.start.row==e&&l.start.column>=c.column&&(l.start.column+=h,l.start.row+=g),l.end.row==e&&l.end.column>=c.column&&(l.end.column+=h,l.end.row+=g)}if(g!=0&&j=this.pos.column&&c.start.column<=this.pos.column+this.length+1){var f=c.start.column-this.pos.column;this.length+=e;if(!this.session.$fromUndo){if(b.action==="insertText")for(var g=this.others.length-1;g>=0;g--){var h=this.others[g],i={row:h.row,column:h.column+f};h.row===c.start.row&&c.start.column=0;g--){var h=this.others[g],i={row:h.row,column:h.column+f};h.row===c.start.row&&c.start.column=this.pos.column&&b.column<=this.pos.column+this.length?(this.showOtherMarkers(),this._emit("cursorEnter",a)):(this.hideOtherMarkers(),this._emit("cursorLeave",a))},this.detach=function(){this.session.removeMarker(this.markerId),this.hideOtherMarkers(),this.doc.removeEventListener("change",this.$onUpdate),this.session.selection.removeEventListener("changeCursor",this.$onCursorChange),this.pos.detach();for(var a=0;a1&&h(b,"")>-1&&(i=RegExp(this.source,d.replace.call(g(this),"g","")),d.replace.call(a.slice(b.index),i,function(){for(var a=1;ab.index&&this.lastIndex--}return b},f||(RegExp.prototype.test=function(a){var b=d.exec.call(this,a);return b&&this.global&&!b[0].length&&this.lastIndex>b.index&&this.lastIndex--,!!b})}),define("ace/lib/es5-shim",["require","exports","module"],function(a,b,c){function p(a){try{return Object.defineProperty(a,"sentinel",{}),"sentinel"in a}catch(b){}}Function.prototype.bind||(Function.prototype.bind=function(b){var c=this;if(typeof c!="function")throw new TypeError;var d=g.call(arguments,1),e=function(){if(this instanceof e){var a=function(){};a.prototype=c.prototype;var f=new a,h=c.apply(f,d.concat(g.call(arguments)));return h!==null&&Object(h)===h?h:f}return c.apply(b,d.concat(g.call(arguments)))};return e});var d=Function.prototype.call,e=Array.prototype,f=Object.prototype,g=e.slice,h=d.bind(f.toString),i=d.bind(f.hasOwnProperty),j,k,l,m,n;if(n=i(f,"__defineGetter__"))j=d.bind(f.__defineGetter__),k=d.bind(f.__defineSetter__),l=d.bind(f.__lookupGetter__),m=d.bind(f.__lookupSetter__);Array.isArray||(Array.isArray=function(b){return h(b)=="[object Array]"}),Array.prototype.forEach||(Array.prototype.forEach=function(b){var c=G(this),d=arguments[1],e=0,f=c.length>>>0;if(h(b)!="[object Function]")throw new TypeError;while(e>>0,e=Array(d),f=arguments[1];if(h(b)!="[object Function]")throw new TypeError;for(var g=0;g>>0,e=[],f=arguments[1];if(h(b)!="[object Function]")throw new TypeError;for(var g=0;g>>0,e=arguments[1];if(h(b)!="[object Function]")throw new TypeError;for(var f=0;f>>0,e=arguments[1];if(h(b)!="[object Function]")throw new TypeError;for(var f=0;f>>0;if(h(b)!="[object Function]")throw new TypeError;if(!d&&arguments.length==1)throw new TypeError;var e=0,f;if(arguments.length>=2)f=arguments[1];else do{if(e in c){f=c[e++];break}if(++e>=d)throw new TypeError}while(!0);for(;e>>0;if(h(b)!="[object Function]")throw new TypeError;if(!d&&arguments.length==1)throw new TypeError;var e,f=d-1;if(arguments.length>=2)e=arguments[1];else do{if(f in c){e=c[f--];break}if(--f<0)throw new TypeError}while(!0);do f in this&&(e=b.call(void 0,e,c[f],f,c));while(f--);return e}),Array.prototype.indexOf||(Array.prototype.indexOf=function(b){var c=G(this),d=c.length>>>0;if(!d)return-1;var e=0;arguments.length>1&&(e=E(arguments[1])),e=e>=0?e:Math.max(0,d+e);for(;e>>0;if(!d)return-1;var e=d-1;arguments.length>1&&(e=Math.min(e,E(arguments[1]))),e=e>=0?e:d-Math.abs(e);for(;e>=0;e--)if(e in c&&b===c[e])return e;return-1}),Object.getPrototypeOf||(Object.getPrototypeOf=function(b){return b.__proto__||(b.constructor?b.constructor.prototype:f)});if(!Object.getOwnPropertyDescriptor){var o="Object.getOwnPropertyDescriptor called on a non-object: ";Object.getOwnPropertyDescriptor=function(b,c){if(typeof b!="object"&&typeof b!="function"||b===null)throw new TypeError(o+b);if(!i(b,c))return;var d,e,g;d={enumerable:!0,configurable:!0};if(n){var h=b.__proto__;b.__proto__=f;var e=l(b,c),g=m(b,c);b.__proto__=h;if(e||g)return e&&(d.get=e),g&&(d.set=g),d}return d.value=b[c],d}}Object.getOwnPropertyNames||(Object.getOwnPropertyNames=function(b){return Object.keys(b)}),Object.create||(Object.create=function(b,c){var d;if(b===null)d={__proto__:null};else{if(typeof b!="object")throw new TypeError("typeof prototype["+typeof b+"] != 'object'");var e=function(){};e.prototype=b,d=new e,d.__proto__=b}return c!==void 0&&Object.defineProperties(d,c),d});if(Object.defineProperty){var q=p({}),r=typeof document=="undefined"||p(document.createElement("div"));if(!q||!r)var s=Object.defineProperty}if(!Object.defineProperty||s){var t="Property description must be an object: ",u="Object.defineProperty called on non-object: ",v="getters & setters can not be defined on this javascript engine";Object.defineProperty=function(b,c,d){if(typeof b!="object"&&typeof b!="function"||b===null)throw new TypeError(u+b);if(typeof d!="object"&&typeof d!="function"||d===null)throw new TypeError(t+d);if(s)try{return s.call(Object,b,c,d)}catch(e){}if(i(d,"value"))if(n&&(l(b,c)||m(b,c))){var g=b.__proto__;b.__proto__=f,delete b[c],b[c]=d.value,b.__proto__=g}else b[c]=d.value;else{if(!n)throw new TypeError(v);i(d,"get")&&j(b,c,d.get),i(d,"set")&&k(b,c,d.set)}return b}}Object.defineProperties||(Object.defineProperties=function(b,c){for(var d in c)i(c,d)&&Object.defineProperty(b,d,c[d]);return b}),Object.seal||(Object.seal=function(b){return b}),Object.freeze||(Object.freeze=function(b){return b});try{Object.freeze(function(){})}catch(w){Object.freeze=function(b){return function(c){return typeof c=="function"?c:b(c)}}(Object.freeze)}Object.preventExtensions||(Object.preventExtensions=function(b){return b}),Object.isSealed||(Object.isSealed=function(b){return!1}),Object.isFrozen||(Object.isFrozen=function(b){return!1}),Object.isExtensible||(Object.isExtensible=function(b){if(Object(b)===b)throw new TypeError;var c="";while(i(b,c))c+="?";b[c]=!0;var d=i(b,c);return delete b[c],d});if(!Object.keys){var x=!0,y=["toString","toLocaleString","valueOf","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","constructor"],z=y.length;for(var A in{toString:null})x=!1;Object.keys=function H(a){if(typeof a!="object"&&typeof a!="function"||a===null)throw new TypeError("Object.keys called on a non-object");var H=[];for(var b in a)i(a,b)&&H.push(b);if(x)for(var c=0,d=z;c9999?"+":"")+("00000"+Math.abs(e)).slice(0<=e&&e<=9999?-4:-6),c=b.length;while(c--)d=b[c],d<10&&(b[c]="0"+d);return e+"-"+b.slice(0,2).join("-")+"T"+b.slice(2).join(":")+"."+("000"+this.getUTCMilliseconds()).slice(-3)+"Z"};Date.now||(Date.now=function(){return(new Date).getTime()}),Date.prototype.toJSON||(Date.prototype.toJSON=function(b){if(typeof this.toISOString!="function")throw new TypeError;return this.toISOString()}),Date.parse("+275760-09-13T00:00:00.000Z")!==864e13&&(Date=function(a){var b=function e(b,c,d,f,g,h,i){var j=arguments.length;if(this instanceof a){var k=j==1&&String(b)===b?new a(e.parse(b)):j>=7?new a(b,c,d,f,g,h,i):j>=6?new a(b,c,d,f,g,h):j>=5?new a(b,c,d,f,g):j>=4?new a(b,c,d,f):j>=3?new a(b,c,d):j>=2?new a(b,c):j>=1?new a(b):new a;return k.constructor=e,k}return a.apply(this,arguments)},c=new RegExp("^(\\d{4}|[+-]\\d{6})(?:-(\\d{2})(?:-(\\d{2})(?:T(\\d{2}):(\\d{2})(?::(\\d{2})(?:\\.(\\d{3}))?)?(?:Z|(?:([-+])(\\d{2}):(\\d{2})))?)?)?)?$");for(var d in a)b[d]=a[d];return b.now=a.now,b.UTC=a.UTC,b.prototype=a.prototype,b.prototype.constructor=b,b.parse=function(d){var e=c.exec(d);if(e){e.shift();for(var f=1;f<7;f++)e[f]=+(e[f]||(f<3?1:0)),f==1&&e[f]--;var g=+e.pop(),h=+e.pop(),i=e.pop(),j=0;if(i){if(h>23||g>59)return NaN;j=(h*60+g)*6e4*(i=="+"?-1:1)}var k=+e[0];return 0<=k&&k<=99?(e[0]=k+400,a.UTC.apply(this,e)+j-126227808e5):a.UTC.apply(this,e)+j}return a.parse.apply(this,arguments)},b}(Date));var B=" \n \f\r   ᠎              \u2028\u2029";if(!String.prototype.trim||B.trim()){B="["+B+"]";var C=new RegExp("^"+B+B+"*"),D=new RegExp(B+B+"*$");String.prototype.trim=function(){return String(this).replace(C,"").replace(D,"")}}var E=function(a){return a=+a,a!==a?a=0:a!==0&&a!==1/0&&a!==-Infinity&&(a=(a>0||-1)*Math.floor(Math.abs(a))),a},F="a"[0]!="a",G=function(a){if(a==null)throw new TypeError;return F&&typeof a=="string"&&a?a.split(""):Object(a)}}),define("ace/lib/dom",["require","exports","module"],function(a,b,c){"use strict";var d="http://www.w3.org/1999/xhtml";b.createElement=function(a,b){return document.createElementNS?document.createElementNS(b||d,a):document.createElement(a)},b.setText=function(a,b){a.innerText!==undefined&&(a.innerText=b),a.textContent!==undefined&&(a.textContent=b)},b.hasCssClass=function(a,b){var c=a.className.split(/\s+/g);return c.indexOf(b)!==-1},b.addCssClass=function(a,c){b.hasCssClass(a,c)||(a.className+=" "+c)},b.removeCssClass=function(a,b){var c=a.className.split(/\s+/g);for(;;){var d=c.indexOf(b);if(d==-1)break;c.splice(d,1)}a.className=c.join(" ")},b.toggleCssClass=function(a,b){var c=a.className.split(/\s+/g),d=!0;for(;;){var e=c.indexOf(b);if(e==-1)break;d=!1,c.splice(e,1)}return d&&c.push(b),a.className=c.join(" "),d},b.setCssClass=function(a,c,d){d?b.addCssClass(a,c):b.removeCssClass(a,c)},b.hasCssString=function(a,b){var c=0,d;b=b||document;if(b.createStyleSheet&&(d=b.styleSheets)){while(c5||Math.abs(a.clientY-j)>5)h=0;h==d&&(h=0,g(a));if(e)return b.preventDefault(a)};b.addListener(a,"mousedown",k),e.isOldIE&&b.addListener(a,"dblclick",k)},b.addCommandKeyListener=function(a,c){var d=b.addListener;if(e.isOldGecko||e.isOpera){var f=null;d(a,"keydown",function(a){f=a.keyCode}),d(a,"keypress",function(a){return g(c,a,f)})}else{var h=null;d(a,"keydown",function(a){return h=a.keyIdentifier||a.keyCode,g(c,a,a.keyCode)})}};if(window.postMessage){var h=1;b.nextTick=function(a,c){c=c||window;var d="zero-timeout-message-"+h;b.addListener(c,"message",function e(f){f.data==d&&(b.stopPropagation(f),b.removeListener(c,"message",e),a())}),c.postMessage(d,"*")}}else b.nextTick=function(a,b){b=b||window,window.setTimeout(a,0)}}),define("ace/lib/keys",["require","exports","module","ace/lib/oop"],function(a,b,c){"use strict";var d=a("./oop"),e=function(){var a={MODIFIER_KEYS:{16:"Shift",17:"Ctrl",18:"Alt",224:"Meta"},KEY_MODS:{ctrl:1,alt:2,option:2,shift:4,meta:8,command:8},FUNCTION_KEYS:{8:"Backspace",9:"Tab",13:"Return",19:"Pause",27:"Esc",32:"Space",33:"PageUp",34:"PageDown",35:"End",36:"Home",37:"Left",38:"Up",39:"Right",40:"Down",44:"Print",45:"Insert",46:"Delete",96:"Numpad0",97:"Numpad1",98:"Numpad2",99:"Numpad3",100:"Numpad4",101:"Numpad5",102:"Numpad6",103:"Numpad7",104:"Numpad8",105:"Numpad9",112:"F1",113:"F2",114:"F3",115:"F4",116:"F5",117:"F6",118:"F7",119:"F8",120:"F9",121:"F10",122:"F11",123:"F12",144:"Numlock",145:"Scrolllock"},PRINTABLE_KEYS:{32:" ",48:"0",49:"1",50:"2",51:"3",52:"4",53:"5",54:"6",55:"7",56:"8",57:"9",59:";",61:"=",65:"a",66:"b",67:"c",68:"d",69:"e",70:"f",71:"g",72:"h",73:"i",74:"j",75:"k",76:"l",77:"m",78:"n",79:"o",80:"p",81:"q",82:"r",83:"s",84:"t",85:"u",86:"v",87:"w",88:"x",89:"y",90:"z",107:"+",109:"-",110:".",188:",",190:".",191:"/",192:"`",219:"[",220:"\\",221:"]",222:'"'}};for(var b in a.FUNCTION_KEYS){var c=a.FUNCTION_KEYS[b].toUpperCase();a[c]=parseInt(b,10)}return d.mixin(a,a.MODIFIER_KEYS),d.mixin(a,a.PRINTABLE_KEYS),d.mixin(a,a.FUNCTION_KEYS),a}();d.mixin(b,e),b.keyCodeToString=function(a){return(e[a]||String.fromCharCode(a)).toLowerCase()}}),define("ace/lib/oop",["require","exports","module"],function(a,b,c){"use strict",b.inherits=function(){var a=function(){};return function(b,c){a.prototype=c.prototype,b.super_=c.prototype,b.prototype=new a,b.prototype.constructor=b}}(),b.mixin=function(a,b){for(var c in b)a[c]=b[c]},b.implement=function(a,c){b.mixin(a,c)}}),define("ace/lib/useragent",["require","exports","module"],function(a,b,c){"use strict";var d=(navigator.platform.match(/mac|win|linux/i)||["other"])[0].toLowerCase(),e=navigator.userAgent;b.isWin=d=="win",b.isMac=d=="mac",b.isLinux=d=="linux",b.isIE=navigator.appName=="Microsoft Internet Explorer"&&parseFloat(navigator.userAgent.match(/MSIE ([0-9]+[\.0-9]+)/)[1]),b.isOldIE=b.isIE&&b.isIE<9,b.isGecko=b.isMozilla=window.controllers&&window.navigator.product==="Gecko",b.isOldGecko=b.isGecko&&parseInt((navigator.userAgent.match(/rv\:(\d+)/)||[])[1],10)<4,b.isOpera=window.opera&&Object.prototype.toString.call(window.opera)=="[object Opera]",b.isWebKit=parseFloat(e.split("WebKit/")[1])||undefined,b.isChrome=parseFloat(e.split(" Chrome/")[1])||undefined,b.isAIR=e.indexOf("AdobeAIR")>=0,b.isIPad=e.indexOf("iPad")>=0,b.isTouchPad=e.indexOf("TouchPad")>=0,b.OS={LINUX:"LINUX",MAC:"MAC",WINDOWS:"WINDOWS"},b.getOS=function(){return b.isMac?b.OS.MAC:b.isLinux?b.OS.LINUX:b.OS.WINDOWS}}),define("ace/editor",["require","exports","module","ace/lib/fixoldbrowsers","ace/lib/oop","ace/lib/lang","ace/lib/useragent","ace/keyboard/textinput","ace/mouse/mouse_handler","ace/mouse/fold_handler","ace/keyboard/keybinding","ace/edit_session","ace/search","ace/range","ace/lib/event_emitter","ace/commands/command_manager","ace/commands/default_commands"],function(a,b,c){"use strict",a("./lib/fixoldbrowsers");var d=a("./lib/oop"),e=a("./lib/lang"),f=a("./lib/useragent"),g=a("./keyboard/textinput").TextInput,h=a("./mouse/mouse_handler").MouseHandler,i=a("./mouse/fold_handler").FoldHandler,j=a("./keyboard/keybinding").KeyBinding,k=a("./edit_session").EditSession,l=a("./search").Search,m=a("./range").Range,n=a("./lib/event_emitter").EventEmitter,o=a("./commands/command_manager").CommandManager,p=a("./commands/default_commands").commands,q=function(a,b){var c=a.getContainerElement();this.container=c,this.renderer=a,this.textInput=new g(a.getTextAreaContainer(),this),this.keyBinding=new j(this),f.isIPad||(this.$mouseHandler=new h(this),new i(this)),this.$blockScrolling=0,this.$search=(new l).set({wrap:!0}),this.commands=new o(f.isMac?"mac":"win",p),this.setSession(b||new k(""))};(function(){d.implement(this,n),this.setKeyboardHandler=function(a){this.keyBinding.setKeyboardHandler(a)},this.getKeyboardHandler=function(){return this.keyBinding.getKeyboardHandler()},this.setSession=function(a){if(this.session==a)return;if(this.session){var b=this.session;this.session.removeEventListener("change",this.$onDocumentChange),this.session.removeEventListener("changeMode",this.$onChangeMode),this.session.removeEventListener("tokenizerUpdate",this.$onTokenizerUpdate),this.session.removeEventListener("changeTabSize",this.$onChangeTabSize),this.session.removeEventListener("changeWrapLimit",this.$onChangeWrapLimit),this.session.removeEventListener("changeWrapMode",this.$onChangeWrapMode),this.session.removeEventListener("onChangeFold",this.$onChangeFold),this.session.removeEventListener("changeFrontMarker",this.$onChangeFrontMarker),this.session.removeEventListener("changeBackMarker",this.$onChangeBackMarker),this.session.removeEventListener("changeBreakpoint",this.$onChangeBreakpoint),this.session.removeEventListener("changeAnnotation",this.$onChangeAnnotation),this.session.removeEventListener("changeOverwrite",this.$onCursorChange),this.session.removeEventListener("changeScrollTop",this.$onScrollTopChange),this.session.removeEventListener("changeLeftTop",this.$onScrollLeftChange);var c=this.session.getSelection();c.removeEventListener("changeCursor",this.$onCursorChange),c.removeEventListener("changeSelection",this.$onSelectionChange)}this.session=a,this.$onDocumentChange=this.onDocumentChange.bind(this),a.addEventListener("change",this.$onDocumentChange),this.renderer.setSession(a),this.$onChangeMode=this.onChangeMode.bind(this),a.addEventListener("changeMode",this.$onChangeMode),this.$onTokenizerUpdate=this.onTokenizerUpdate.bind(this),a.addEventListener("tokenizerUpdate",this.$onTokenizerUpdate),this.$onChangeTabSize=this.renderer.updateText.bind(this.renderer),a.addEventListener("changeTabSize",this.$onChangeTabSize),this.$onChangeWrapLimit=this.onChangeWrapLimit.bind(this),a.addEventListener("changeWrapLimit",this.$onChangeWrapLimit),this.$onChangeWrapMode=this.onChangeWrapMode.bind(this),a.addEventListener("changeWrapMode",this.$onChangeWrapMode),this.$onChangeFold=this.onChangeFold.bind(this),a.addEventListener("changeFold",this.$onChangeFold),this.$onChangeFrontMarker=this.onChangeFrontMarker.bind(this),this.session.addEventListener("changeFrontMarker",this.$onChangeFrontMarker),this.$onChangeBackMarker=this.onChangeBackMarker.bind(this),this.session.addEventListener("changeBackMarker",this.$onChangeBackMarker),this.$onChangeBreakpoint=this.onChangeBreakpoint.bind(this),this.session.addEventListener("changeBreakpoint",this.$onChangeBreakpoint),this.$onChangeAnnotation=this.onChangeAnnotation.bind(this),this.session.addEventListener("changeAnnotation",this.$onChangeAnnotation),this.$onCursorChange=this.onCursorChange.bind(this),this.session.addEventListener("changeOverwrite",this.$onCursorChange),this.$onScrollTopChange=this.onScrollTopChange.bind(this),this.session.addEventListener("changeScrollTop",this.$onScrollTopChange),this.$onScrollLeftChange=this.onScrollLeftChange.bind(this),this.session.addEventListener("changeScrollLeft",this.$onScrollLeftChange),this.selection=a.getSelection(),this.selection.addEventListener("changeCursor",this.$onCursorChange),this.$onSelectionChange=this.onSelectionChange.bind(this),this.selection.addEventListener("changeSelection",this.$onSelectionChange),this.onChangeMode(),this.$blockScrolling+=1,this.onCursorChange(),this.$blockScrolling-=1,this.onScrollTopChange(),this.onScrollLeftChange(),this.onSelectionChange(),this.onChangeFrontMarker(),this.onChangeBackMarker(),this.onChangeBreakpoint(),this.onChangeAnnotation(),this.session.getUseWrapMode()&&this.renderer.adjustWrapLimit(),this.renderer.updateFull(),this._emit("changeSession",{session:a,oldSession:b})},this.getSession=function(){return this.session},this.getSelection=function(){return this.selection},this.resize=function(){this.renderer.onResize()},this.setTheme=function(a){this.renderer.setTheme(a)},this.getTheme=function(){return this.renderer.getTheme()},this.setStyle=function(a){this.renderer.setStyle(a)},this.unsetStyle=function(a){this.renderer.unsetStyle(a)},this.setFontSize=function(a){this.container.style.fontSize=a,this.renderer.updateFontSize()},this.$highlightBrackets=function(){this.session.$bracketHighlight&&(this.session.removeMarker(this.session.$bracketHighlight),this.session.$bracketHighlight=null);if(this.$highlightPending)return;var a=this;this.$highlightPending=!0,setTimeout(function(){a.$highlightPending=!1;var b=a.session.findMatchingBracket(a.getCursorPosition());if(b){var c=new m(b.row,b.column,b.row,b.column+1);a.session.$bracketHighlight=a.session.addMarker(c,"ace_bracket","text")}},10)},this.focus=function(){var a=this;setTimeout(function(){a.textInput.focus()}),this.textInput.focus()},this.isFocused=function(){return this.textInput.isFocused()},this.blur=function(){this.textInput.blur()},this.onFocus=function(){this.renderer.showCursor(),this.renderer.visualizeFocus(),this._emit("focus")},this.onBlur=function(){this.renderer.hideCursor(),this.renderer.visualizeBlur(),this._emit("blur")},this.onDocumentChange=function(a){var b=a.data,c=b.range,d;c.start.row==c.end.row&&b.action!="insertLines"&&b.action!="removeLines"?d=c.end.row:d=Infinity,this.renderer.updateLines(c.start.row,d),this._emit("change",a),this.onCursorChange()},this.onTokenizerUpdate=function(a){var b=a.data;this.renderer.updateLines(b.first,b.last)},this.onScrollTopChange=function(){this.renderer.scrollToY(this.session.getScrollTop())},this.onScrollLeftChange=function(){this.renderer.scrollToX(this.session.getScrollLeft())},this.onCursorChange=function(){this.renderer.updateCursor(),this.$blockScrolling||this.renderer.scrollCursorIntoView(),this.renderer.moveTextAreaToCursor(this.textInput.getElement()),this.$highlightBrackets(),this.$updateHighlightActiveLine()},this.$updateHighlightActiveLine=function(){var a=this.getSession();a.$highlightLineMarker&&a.removeMarker(a.$highlightLineMarker),typeof this.$lastrow=="number"&&this.renderer.removeGutterDecoration(this.$lastrow,"ace_gutter_active_line"),a.$highlightLineMarker=null,this.$lastrow=null;if(this.getHighlightActiveLine()){var b=this.getCursorPosition(),c=this.session.getFoldLine(b.row);if(this.getSelectionStyle()!="line"||!this.selection.isMultiLine()){var d;c?d=new m(c.start.row,0,c.end.row+1,0):d=new m(b.row,0,b.row+1,0),a.$highlightLineMarker=a.addMarker(d,"ace_active_line","background")}this.renderer.addGutterDecoration(this.$lastrow=b.row,"ace_gutter_active_line")}},this.onSelectionChange=function(a){var b=this.getSession();b.$selectionMarker&&b.removeMarker(b.$selectionMarker),b.$selectionMarker=null;if(!this.selection.isEmpty()){var c=this.selection.getRange(),d=this.getSelectionStyle();b.$selectionMarker=b.addMarker(c,"ace_selection",d)}else this.$updateHighlightActiveLine();this.$highlightSelectedWord&&this.session.getMode().highlightSelection(this)},this.onChangeFrontMarker=function(){this.renderer.updateFrontMarkers()},this.onChangeBackMarker=function(){this.renderer.updateBackMarkers()},this.onChangeBreakpoint=function(){this.renderer.setBreakpoints(this.session.getBreakpoints())},this.onChangeAnnotation=function(){this.renderer.setAnnotations(this.session.getAnnotations())},this.onChangeMode=function(){this.renderer.updateText()},this.onChangeWrapLimit=function(){this.renderer.updateFull()},this.onChangeWrapMode=function(){this.renderer.onResize(!0)},this.onChangeFold=function(){this.$updateHighlightActiveLine(),this.renderer.updateFull()},this.getCopyText=function(){var a="";return this.selection.isEmpty()||(a=this.session.getTextRange(this.getSelectionRange())),this._emit("copy",a),a},this.onCut=function(){this.commands.exec("cut",this)},this.insert=function(a){var b=this.session,c=b.getMode(),d=this.getCursorPosition();if(this.getBehavioursEnabled()){var e=c.transformAction(b.getState(d.row),"insertion",this,b,a);e&&(a=e.text)}a=a.replace(" ",this.session.getTabString());if(!this.selection.isEmpty())d=this.session.remove(this.getSelectionRange()),this.clearSelection();else if(this.session.getOverwrite()){var f=new m.fromPoints(d,d);f.end.column+=a.length,this.session.remove(f)}this.clearSelection();var g=d.column,h=b.getState(d.row),i=c.checkOutdent(h,b.getLine(d.row),a),j=b.getLine(d.row),k=c.getNextLineIndent(h,j.slice(0,d.column),b.getTabString()),l=b.insert(d,a);e&&e.selection&&(e.selection.length==2?this.selection.setSelectionRange(new m(d.row,g+e.selection[0],d.row,g+e.selection[1])):this.selection.setSelectionRange(new m(d.row+e.selection[0],e.selection[1],d.row+e.selection[2],e.selection[3])));var h=b.getState(d.row);if(b.getDocument().isNewLine(a)){this.moveCursorTo(d.row+1,0);var n=b.getTabSize(),o=Number.MAX_VALUE;for(var p=d.row+1;p<=l.row;++p){var q=0;j=b.getLine(p);for(var r=0;r0;++r)j.charAt(r)==" "?s-=n:j.charAt(r)==" "&&(s-=1);b.remove(new m(p,0,p,r))}b.indentRows(d.row+1,l.row,k)}i&&c.autoOutdent(h,b,d.row)},this.onTextInput=function(a,b){b&&this._emit("paste",a),this.keyBinding.onTextInput(a,b)},this.onCommandKey=function(a,b,c){this.keyBinding.onCommandKey(a,b,c)},this.setOverwrite=function(a){this.session.setOverwrite(a)},this.getOverwrite=function(){return this.session.getOverwrite()},this.toggleOverwrite=function(){this.session.toggleOverwrite()},this.setScrollSpeed=function(a){this.$mouseHandler.setScrollSpeed(a)},this.getScrollSpeed=function(){return this.$mouseHandler.getScrollSpeed()},this.setDragDelay=function(a){this.$mouseHandler.setDragDelay(a)},this.getDragDelay=function(){return this.$mouseHandler.getDragDelay()},this.$selectionStyle="line",this.setSelectionStyle=function(a){if(this.$selectionStyle==a)return;this.$selectionStyle=a,this.onSelectionChange(),this._emit("changeSelectionStyle",{data:a})},this.getSelectionStyle=function(){return this.$selectionStyle},this.$highlightActiveLine=!0,this.setHighlightActiveLine=function(a){if(this.$highlightActiveLine==a)return;this.$highlightActiveLine=a,this.$updateHighlightActiveLine()},this.getHighlightActiveLine=function(){return this.$highlightActiveLine},this.$highlightSelectedWord=!0,this.setHighlightSelectedWord=function(a){if(this.$highlightSelectedWord==a)return;this.$highlightSelectedWord=a,a?this.session.getMode().highlightSelection(this):this.session.getMode().clearSelectionHighlight(this)},this.getHighlightSelectedWord=function(){return this.$highlightSelectedWord},this.setAnimatedScroll=function(a){this.renderer.setAnimatedScroll(a)},this.getAnimatedScroll=function(){return this.renderer.getAnimatedScroll()},this.setShowInvisibles=function(a){if(this.getShowInvisibles()==a)return;this.renderer.setShowInvisibles(a)},this.getShowInvisibles=function(){return this.renderer.getShowInvisibles()},this.setShowPrintMargin=function(a){this.renderer.setShowPrintMargin(a)},this.getShowPrintMargin=function(){return this.renderer.getShowPrintMargin()},this.setPrintMarginColumn=function(a){this.renderer.setPrintMarginColumn(a)},this.getPrintMarginColumn=function(){return this.renderer.getPrintMarginColumn()},this.$readOnly=!1,this.setReadOnly=function(a){this.$readOnly=a},this.getReadOnly=function(){return this.$readOnly},this.$modeBehaviours=!0,this.setBehavioursEnabled=function(a){this.$modeBehaviours=a},this.getBehavioursEnabled=function(){return this.$modeBehaviours},this.setShowFoldWidgets=function(a){var b=this.renderer.$gutterLayer;if(b.getShowFoldWidgets()==a)return;this.renderer.$gutterLayer.setShowFoldWidgets(a),this.$showFoldWidgets=a,this.renderer.updateFull()},this.getShowFoldWidgets=function(){return this.renderer.$gutterLayer.getShowFoldWidgets()},this.remove=function(a){this.selection.isEmpty()&&(a=="left"?this.selection.selectLeft():this.selection.selectRight());var b=this.getSelectionRange();if(this.getBehavioursEnabled()){var c=this.session,d=c.getState(b.start.row),e=c.getMode().transformAction(d,"deletion",this,c,b);e&&(b=e)}this.session.remove(b),this.clearSelection()},this.removeWordRight=function(){this.selection.isEmpty()&&this.selection.selectWordRight(),this.session.remove(this.getSelectionRange()),this.clearSelection()},this.removeWordLeft=function(){this.selection.isEmpty()&&this.selection.selectWordLeft(),this.session.remove(this.getSelectionRange()),this.clearSelection()},this.removeToLineStart=function(){this.selection.isEmpty()&&this.selection.selectLineStart(),this.session.remove(this.getSelectionRange()),this.clearSelection()},this.removeToLineEnd=function(){this.selection.isEmpty()&&this.selection.selectLineEnd();var a=this.getSelectionRange();a.start.column==a.end.column&&a.start.row==a.end.row&&(a.end.column=0,a.end.row++),this.session.remove(a),this.clearSelection()},this.splitLine=function(){this.selection.isEmpty()||(this.session.remove(this.getSelectionRange()),this.clearSelection());var a=this.getCursorPosition();this.insert("\n"),this.moveCursorToPosition(a)},this.transposeLetters=function(){if(!this.selection.isEmpty())return;var a=this.getCursorPosition(),b=a.column;if(b===0)return;var c=this.session.getLine(a.row),d,e;b=this.getFirstVisibleRow()&&a<=this.getLastVisibleRow()},this.isRowFullyVisible=function(a){return a>=this.renderer.getFirstFullyVisibleRow()&&a<=this.renderer.getLastFullyVisibleRow()},this.$getVisibleRowCount=function(){return this.renderer.getScrollBottomRow()-this.renderer.getScrollTopRow()+1},this.$getPageDownRow=function(){return this.renderer.getScrollBottomRow()},this.$getPageUpRow=function(){var a=this.renderer.getScrollTopRow(),b=this.renderer.getScrollBottomRow();return a-(b-a)},this.selectPageDown=function(){var a=this.$getPageDownRow()+Math.floor(this.$getVisibleRowCount()/2);this.scrollPageDown();var b=this.getSelection(),c=this.session.documentToScreenPosition(b.getSelectionLead()),d=this.session.screenToDocumentPosition(a,c.column);b.selectTo(d.row,d.column)},this.selectPageUp=function(){var a=this.renderer.getScrollTopRow()-this.renderer.getScrollBottomRow(),b=this.$getPageUpRow()+Math.round(a/2);this.scrollPageUp();var c=this.getSelection(),d=this.session.documentToScreenPosition(c.getSelectionLead()),e=this.session.screenToDocumentPosition(b,d.column);c.selectTo(e.row,e.column)},this.gotoPageDown=function(){var a=this.$getPageDownRow(),b=this.getCursorPositionScreen().column;this.scrollToRow(a),this.getSelection().moveCursorToScreen(a,b)},this.gotoPageUp=function(){var a=this.$getPageUpRow(),b=this.getCursorPositionScreen().column;this.scrollToRow(a),this.getSelection().moveCursorToScreen(a,b)},this.scrollPageDown=function(){this.scrollToRow(this.$getPageDownRow())},this.scrollPageUp=function(){this.renderer.scrollToRow(this.$getPageUpRow())},this.scrollToRow=function(a){this.renderer.scrollToRow(a)},this.scrollToLine=function(a,b){this.renderer.scrollToLine(a,b)},this.centerSelection=function(){var a=this.getSelectionRange(),b=Math.floor(a.start.row+(a.end.row-a.start.row)/2);this.renderer.scrollToLine(b,!0)},this.getCursorPosition=function(){return this.selection.getCursor()},this.getCursorPositionScreen=function(){return this.session.documentToScreenPosition(this.getCursorPosition())},this.getSelectionRange=function(){return this.selection.getRange()},this.selectAll=function(){this.$blockScrolling+=1,this.selection.selectAll(),this.$blockScrolling-=1},this.clearSelection=function(){this.selection.clearSelection()},this.moveCursorTo=function(a,b){this.selection.moveCursorTo(a,b)},this.moveCursorToPosition=function(a){this.selection.moveCursorToPosition(a)},this.jumpToMatching=function(){var a=this.getCursorPosition(),b=this.session.findMatchingBracket(a);b||(a.column+=1,b=this.session.findMatchingBracket(a)),b||(a.column-=2,b=this.session.findMatchingBracket(a)),b&&(this.clearSelection(),this.moveCursorTo(b.row,b.column))},this.gotoLine=function(a,b){this.selection.clearSelection(),this.session.unfold({row:a-1,column:b||0}),this.$blockScrolling+=1,this.moveCursorTo(a-1,b||0),this.$blockScrolling-=1,this.isRowFullyVisible(this.getCursorPosition().row)||this.scrollToLine(a,!0)},this.navigateTo=function(a,b){this.clearSelection(),this.moveCursorTo(a,b)},this.navigateUp=function(a){this.selection.clearSelection(),a=a||1,this.selection.moveCursorBy(-a,0)},this.navigateDown=function(a){this.selection.clearSelection(),a=a||1,this.selection.moveCursorBy(a,0)},this.navigateLeft=function(a){if(!this.selection.isEmpty()){var b=this.getSelectionRange().start;this.moveCursorToPosition(b)}else{a=a||1;while(a--)this.selection.moveCursorLeft()}this.clearSelection()},this.navigateRight=function(a){if(!this.selection.isEmpty()){var b=this.getSelectionRange().end;this.moveCursorToPosition(b)}else{a=a||1;while(a--)this.selection.moveCursorRight()}this.clearSelection()},this.navigateLineStart=function(){this.selection.moveCursorLineStart(),this.clearSelection()},this.navigateLineEnd=function(){this.selection.moveCursorLineEnd(),this.clearSelection()},this.navigateFileEnd=function(){this.selection.moveCursorFileEnd(),this.clearSelection()},this.navigateFileStart=function(){this.selection.moveCursorFileStart(),this.clearSelection()},this.navigateWordRight=function(){this.selection.moveCursorWordRight(),this.clearSelection()},this.navigateWordLeft=function(){this.selection.moveCursorWordLeft(),this.clearSelection()},this.replace=function(a,b){b&&this.$search.set(b);var c=this.$search.find(this.session),d=0;return c?(this.$tryReplace(c,a)&&(d=1),c!==null&&(this.selection.setSelectionRange(c),this.renderer.scrollSelectionIntoView(c.start,c.end)),d):d},this.replaceAll=function(a,b){b&&this.$search.set(b);var c=this.$search.findAll(this.session),d=0;if(!c.length)return d;var e=this.getSelectionRange();this.clearSelection(),this.selection.moveCursorTo(0,0),this.$blockScrolling+=1;for(var f=c.length-1;f>=0;--f)this.$tryReplace(c[f],a)&&d++;return this.selection.setSelectionRange(e),this.$blockScrolling-=1,d},this.$tryReplace=function(a,b){var c=this.session.getTextRange(a);return b=this.$search.replace(c,b),b!==null?(a.end=this.session.replace(a,b),a):null},this.getLastSearchOptions=function(){return this.$search.getOptions()},this.find=function(a,b){this.clearSelection(),b=b||{},b.needle=a,this.$search.set(b),this.$find()},this.findNext=function(a){a=a||{},typeof a.backwards=="undefined"&&(a.backwards=!1),this.$search.set(a),this.$find()},this.findPrevious=function(a){a=a||{},typeof a.backwards=="undefined"&&(a.backwards=!0),this.$search.set(a),this.$find()},this.$find=function(a){this.selection.isEmpty()||this.$search.set({needle:this.session.getTextRange(this.getSelectionRange())}),typeof a!="undefined"&&this.$search.set({backwards:a});var b=this.$search.find(this.session);if(b){this.session.unfold(b),this.$blockScrolling+=1,this.selection.setSelectionRange(b),this.$blockScrolling-=1;if(this.getAnimatedScroll()){var c=this.getCursorPosition();this.isRowFullyVisible(c.row)||this.scrollToLine(c.row,!0)}else this.renderer.scrollSelectionIntoView(b.start,b.end)}},this.undo=function(){this.session.getUndoManager().undo()},this.redo=function(){this.session.getUndoManager().redo()},this.destroy=function(){this.renderer.destroy()}}).call(q.prototype),b.Editor=q}),define("ace/lib/lang",["require","exports","module"],function(a,b,c){"use strict",b.stringReverse=function(a){return a.split("").reverse().join("")},b.stringRepeat=function(a,b){return(new Array(b+1)).join(a)};var d=/^\s\s*/,e=/\s\s*$/;b.stringTrimLeft=function(a){return a.replace(d,"")},b.stringTrimRight=function(a){return a.replace(e,"")},b.copyObject=function(a){var b={};for(var c in a)b[c]=a[c];return b},b.copyArray=function(a){var b=[];for(var c=0,d=a.length;c128)return;setTimeout(function(){h||m()},0)},p=function(a){h=!0,b.onCompositionStart(),e.isGecko||setTimeout(q,0)},q=function(){if(!h)return;b.onCompositionUpdate(c.value)},r=function(a){h=!1,b.onCompositionEnd()},s=function(a){i=!0;var d=b.getCopyText();d?c.value=d:a.preventDefault(),l(),setTimeout(function(){m()},0)},t=function(a){i=!0;var d=b.getCopyText();d?(c.value=d,b.onCut()):a.preventDefault(),l(),setTimeout(function(){m()},0)};d.addCommandKeyListener(c,b.onCommandKey.bind(b));if(e.isOldIE){var u={13:1,27:1};d.addListener(c,"keyup",function(a){h&&(!c.value||u[a.keyCode])&&setTimeout(r,0);if((c.value.charCodeAt(0)|0)<129)return;h?q():p()})}"onpropertychange"in c&&!("oninput"in c)?d.addListener(c,"propertychange",o):d.addListener(c,"input",n),d.addListener(c,"paste",function(a){j=!0,a.clipboardData&&a.clipboardData.getData?(m(a.clipboardData.getData("text/plain")),a.preventDefault()):o()}),"onbeforecopy"in c&&typeof clipboardData!="undefined"?(d.addListener(c,"beforecopy",function(a){var c=b.getCopyText();c?clipboardData.setData("Text",c):a.preventDefault()}),d.addListener(a,"keydown",function(a){if(a.ctrlKey&&a.keyCode==88){var c=b.getCopyText();c&&(clipboardData.setData("Text",c),b.onCut()),d.preventDefault(a)}})):(d.addListener(c,"copy",s),d.addListener(c,"cut",t)),d.addListener(c,"compositionstart",p),e.isGecko&&d.addListener(c,"text",q),e.isWebKit&&d.addListener(c,"keyup",q),d.addListener(c,"compositionend",r),d.addListener(c,"blur",function(){b.onBlur()}),d.addListener(c,"focus",function(){b.onFocus(),l()}),this.focus=function(){b.onFocus(),l(),c.focus()},this.blur=function(){c.blur()},this.isFocused=v,this.getElement=function(){return c},this.onContextMenu=function(a,b){a&&(k||(k=c.style.cssText),c.style.cssText="position:fixed; z-index:1000;left:"+(a.x-2)+"px; top:"+(a.y-2)+"px;"),b&&(c.value="")},this.onContextMenuClose=function(){setTimeout(function(){k&&(c.style.cssText=k,k=""),m()},0)}};b.TextInput=g}),define("ace/mouse/mouse_handler",["require","exports","module","ace/lib/event","ace/mouse/default_handlers","ace/mouse/default_gutter_handler","ace/mouse/mouse_event"],function(a,b,c){"use strict";var d=a("../lib/event"),e=a("./default_handlers").DefaultHandlers,f=a("./default_gutter_handler").GutterHandler,g=a("./mouse_event").MouseEvent,h=function(a){this.editor=a,new e(a),new f(a),d.addListener(a.container,"mousedown",function(b){return a.focus(),d.preventDefault(b)}),d.addListener(a.container,"selectstart",function(a){return d.preventDefault(a)});var b=a.renderer.getMouseEventTarget();d.addListener(b,"mousedown",this.onMouseEvent.bind(this,"mousedown")),d.addListener(b,"click",this.onMouseEvent.bind(this,"click")),d.addListener(b,"mousemove",this.onMouseMove.bind(this,"mousemove")),d.addMultiMouseDownListener(b,0,2,500,this.onMouseEvent.bind(this,"dblclick")),d.addMultiMouseDownListener(b,0,3,600,this.onMouseEvent.bind(this,"tripleclick")),d.addMultiMouseDownListener(b,0,4,600,this.onMouseEvent.bind(this,"quadclick")),d.addMouseWheelListener(a.container,this.onMouseWheel.bind(this,"mousewheel"));var c=a.renderer.$gutter;d.addListener(c,"mousedown",this.onMouseEvent.bind(this,"guttermousedown")),d.addListener(c,"click",this.onMouseEvent.bind(this,"gutterclick")),d.addListener(c,"dblclick",this.onMouseEvent.bind(this,"gutterdblclick")),d.addListener(c,"mousemove",this.onMouseMove.bind(this,"gutter"))};(function(){this.$scrollSpeed=1,this.setScrollSpeed=function(a){this.$scrollSpeed=a},this.getScrollSpeed=function(){return this.$scrollSpeed},this.onMouseEvent=function(a,b){this.editor._emit(a,new g(b,this.editor))},this.$dragDelay=250,this.setDragDelay=function(a){this.$dragDelay=a},this.getDragDelay=function(){return this.$dragDelay},this.onMouseMove=function(a,b){var c=this.editor._eventRegistry&&this.editor._eventRegistry.mousemove;if(!c||!c.length)return;this.editor._emit(a,new g(b,this.editor))},this.onMouseWheel=function(a,b){var c=new g(b,this.editor);c.speed=this.$scrollSpeed*2,c.wheelX=b.wheelX,c.wheelY=b.wheelY,this.editor._emit(a,c)}}).call(h.prototype),b.MouseHandler=h}),define("ace/mouse/default_handlers",["require","exports","module","ace/lib/event","ace/lib/dom","ace/lib/browser_focus"],function(a,b,c){function k(a){this.editor=a,this.$clickSelection=null,this.browserFocus=new f,a.setDefaultHandler("mousedown",this.onMouseDown.bind(this)),a.setDefaultHandler("dblclick",this.onDoubleClick.bind(this)),a.setDefaultHandler("tripleclick",this.onTripleClick.bind(this)),a.setDefaultHandler("quadclick",this.onQuadClick.bind(this)),a.setDefaultHandler("mousewheel",this.onScroll.bind(this))}function l(a,b,c,d){return Math.sqrt(Math.pow(c-a,2)+Math.pow(d-b,2))}"use strict";var d=a("../lib/event"),e=a("../lib/dom"),f=a("../lib/browser_focus").BrowserFocus,g=0,h=1,i=2,j=5;(function(){this.onMouseDown=function(a){function C(b){a.getShiftKey()?m.selection.selectToPosition(b):n.$clickSelection||(m.moveCursorToPosition(b),m.selection.clearSelection()),q=h}var b=a.inSelection(),c=a.pageX,f=a.pageY,k=a.getDocumentPosition(),m=this.editor,n=this,o=m.getSelectionRange(),p=o.isEmpty(),q=g;if(b&&(!this.browserFocus.isFocused()||(new Date).getTime()-this.browserFocus.lastFocus<20||!m.isFocused())){m.focus();return}var r=a.getButton();if(r!==0){p&&m.moveCursorToPosition(k),r==2&&(m.textInput.onContextMenu({x:a.clientX,y:a.clientY},p),d.capture(m.container,function(){},m.textInput.onContextMenuClose));return}b||C(k);var s=c,t=f,u=(new Date).getTime(),v,w,x,y=function(a){s=d.getDocumentX(a),t=d.getDocumentY(a)},z=function(a){clearInterval(F),q==g?C(k):q==i&&A(a),n.$clickSelection=null,q=g},A=function(a){e.removeCssClass(m.container,"ace_dragging"),m.session.removeMarker(x),m.$mouseHandler.$clickSelection||v||(m.moveCursorToPosition(k),m.selection.clearSelection());if(!v)return;if(w.contains(v.row,v.column)){v=null;return}m.clearSelection();if(a&&(a.ctrlKey||a.altKey))var b=m.session,c=b.insert(v,b.getTextRange(w));else var c=m.moveText(w,v);if(!c){v=null;return}m.selection.setSelectionRange(c)},B=function(){if(q==g){var a=l(c,f,s,t),b=(new Date).getTime();if(a>j){q=h;var d=m.renderer.screenToTextCoordinates(s,t);C(d)}else if(b-u>m.getDragDelay()){q=i,w=m.getSelectionRange();var k=m.getSelectionStyle();x=m.session.addMarker(w,"ace_selection",k),m.clearSelection(),e.addCssClass(m.container,"ace_dragging")}}q==i?E():q==h&&D()},D=function(){var a,b=m.renderer.screenToTextCoordinates(s,t);n.$clickSelection?n.$clickSelection.contains(b.row,b.column)?m.selection.setSelectionRange(n.$clickSelection):(n.$clickSelection.compare(b.row,b.column)==-1?a=n.$clickSelection.end:a=n.$clickSelection.start,m.selection.setSelectionAnchor(a.row,a.column),m.selection.selectToPosition(b)):m.selection.selectToPosition(b),m.renderer.scrollCursorIntoView()},E=function(){v=m.renderer.screenToTextCoordinates(s,t),m.moveCursorToPosition(v)};d.capture(m.container,y,z);var F=setInterval(B,20);return a.preventDefault()},this.onDoubleClick=function(a){var b=a.getDocumentPosition(),c=this.editor;c.moveCursorToPosition(b),c.selection.selectWord(),this.$clickSelection=c.getSelectionRange()},this.onTripleClick=function(a){var b=a.getDocumentPosition(),c=this.editor;c.moveCursorToPosition(b),c.selection.selectLine(),this.$clickSelection=c.getSelectionRange()},this.onQuadClick=function(a){var b=this.editor;b.selectAll(),this.$clickSelection=b.getSelectionRange()},this.onScroll=function(a){var b=this.editor;b.renderer.scrollBy(a.wheelX*a.speed,a.wheelY*a.speed);if(b.renderer.isScrollableBy(a.wheelX*a.speed,a.wheelY*a.speed))return a.preventDefault()}}).call(k.prototype),b.DefaultHandlers=k}),define("ace/lib/browser_focus",["require","exports","module","ace/lib/oop","ace/lib/event","ace/lib/event_emitter"],function(a,b,c){"use strict";var d=a("./oop"),e=a("./event"),f=a("./event_emitter").EventEmitter,g=function(a){a=a||window,this.lastFocus=(new Date).getTime(),this._isFocused=!0;var b=this;"onfocusin"in a.document?(e.addListener(a.document,"focusin",function(a){b._setFocused(!0)}),e.addListener(a.document,"focusout",function(a){b._setFocused(!!a.toElement)})):(e.addListener(a,"blur",function(a){b._setFocused(!1)}),e.addListener(a,"focus",function(a){b._setFocused(!0)}))};(function(){d.implement(this,f),this.isFocused=function(){return this._isFocused},this._setFocused=function(a){if(this._isFocused==a)return;a&&(this.lastFocus=(new Date).getTime()),this._isFocused=a,this._emit("changeFocus")}}).call(g.prototype),b.BrowserFocus=g}),define("ace/lib/event_emitter",["require","exports","module"],function(a,b,c){"use strict";var d={};d._emit=d._dispatchEvent=function(a,b){this._eventRegistry=this._eventRegistry||{},this._defaultHandlers=this._defaultHandlers||{};var c=this._eventRegistry[a]||[],d=this._defaultHandlers[a];if(!c.length&&!d)return;b=b||{},b.type=a,b.stopPropagation||(b.stopPropagation=function(){this.propagationStopped=!0}),b.preventDefault||(b.preventDefault=function(){this.defaultPrevented=!0});for(var e=0;e=4352&&a<=4447||a>=4515&&a<=4519||a>=4602&&a<=4607||a>=9001&&a<=9002||a>=11904&&a<=11929||a>=11931&&a<=12019||a>=12032&&a<=12245||a>=12272&&a<=12283||a>=12288&&a<=12350||a>=12353&&a<=12438||a>=12441&&a<=12543||a>=12549&&a<=12589||a>=12593&&a<=12686||a>=12688&&a<=12730||a>=12736&&a<=12771||a>=12784&&a<=12830||a>=12832&&a<=12871||a>=12880&&a<=13054||a>=13056&&a<=19903||a>=19968&&a<=42124||a>=42128&&a<=42182||a>=43360&&a<=43388||a>=44032&&a<=55203||a>=55216&&a<=55238||a>=55243&&a<=55291||a>=63744&&a<=64255||a>=65040&&a<=65049||a>=65072&&a<=65106||a>=65108&&a<=65126||a>=65128&&a<=65131||a>=65281&&a<=65376||a>=65504&&a<=65510}e.implement(this,h),this.setDocument=function(a){if(this.doc)throw new Error("Document is already set");this.doc=a,a.on("change",this.onChange.bind(this)),this.on("changeFold",this.onChangeFold.bind(this)),this.bgTokenizer&&(this.bgTokenizer.setDocument(this.getDocument()),this.bgTokenizer.start(0))},this.getDocument=function(){return this.doc},this.$resetRowCache=function(a){if(a==0){this.$rowCache=[];return}var b=this.$rowCache;for(var c=0;c=a){b.splice(c,b.length);return}},this.onChangeFold=function(a){var b=a.data;this.$resetRowCache(b.start.row)},this.onChange=function(a){var b=a.data;this.$modified=!0,this.$resetRowCache(b.range.start.row);var c=this.$updateInternalDataOnChange(a);!this.$fromUndo&&this.$undoManager&&!b.ignore&&(this.$deltasDoc.push(b),c&&c.length!=0&&this.$deltasFold.push({action:"removeFolds",folds:c}),this.$informUndoManager.schedule()),this.bgTokenizer.start(b.range.start.row),this._emit("change",a)},this.setValue=function(a){this.doc.setValue(a),this.selection.moveCursorTo(0,0),this.selection.clearSelection(),this.$resetRowCache(0),this.$deltas=[],this.$deltasDoc=[],this.$deltasFold=[],this.getUndoManager().reset()},this.getValue=this.toString=function(){return this.doc.getValue()},this.getSelection=function(){return this.selection},this.getState=function(a){return this.bgTokenizer.getState(a)},this.getTokens=function(a,b){return this.bgTokenizer.getTokens(a,b)},this.getTokenAt=function(a,b){var c=this.bgTokenizer.getTokens(a,a)[0].tokens,d,e=0;if(b==null)f=c.length-1,e=this.getLine(a).length;else for(var f=0;f=b)break}return d=c[f],d?(d.index=f,d.start=e-d.value.length,d):null},this.setUndoManager=function(a){this.$undoManager=a,this.$resetRowCache(0),this.$deltas=[],this.$deltasDoc=[],this.$deltasFold=[],this.$informUndoManager&&this.$informUndoManager.cancel();if(a){var b=this;this.$syncInformUndoManager=function(){b.$informUndoManager.cancel(),b.$deltasFold.length&&(b.$deltas.push({group:"fold",deltas:b.$deltasFold}),b.$deltasFold=[]),b.$deltasDoc.length&&(b.$deltas.push({group:"doc",deltas:b.$deltasDoc}),b.$deltasDoc=[]),b.$deltas.length>0&&a.execute({action:"aceupdate",args:[b.$deltas,b]}),b.$deltas=[]},this.$informUndoManager=f.deferredCall(this.$syncInformUndoManager)}},this.$defaultUndoManager={undo:function(){},redo:function(){},reset:function(){}},this.getUndoManager=function(){return this.$undoManager||this.$defaultUndoManager},this.getTabString=function(){return this.getUseSoftTabs()?f.stringRepeat(" ",this.getTabSize()):" "},this.$useSoftTabs=!0,this.setUseSoftTabs=function(a){if(this.$useSoftTabs===a)return;this.$useSoftTabs=a},this.getUseSoftTabs=function(){return this.$useSoftTabs},this.$tabSize=4,this.setTabSize=function(a){if(isNaN(a)||this.$tabSize===a)return;this.$modified=!0,this.$tabSize=a,this._emit("changeTabSize")},this.getTabSize=function(){return this.$tabSize},this.isTabStop=function(a){return this.$useSoftTabs&&a.column%this.$tabSize==0},this.$overwrite=!1,this.setOverwrite=function(a){if(this.$overwrite==a)return;this.$overwrite=a,this._emit("changeOverwrite")},this.getOverwrite=function(){return this.$overwrite},this.toggleOverwrite=function(){this.setOverwrite(!this.$overwrite)},this.getBreakpoints=function(){return this.$breakpoints},this.setBreakpoints=function(a){this.$breakpoints=[];for(var b=0;b0&&(d=!!c.charAt(b-1).match(this.tokenRe)),d||(d=!!c.charAt(b).match(this.tokenRe));var e=d?this.tokenRe:this.nonTokenRe,f=b;if(f>0){do f--;while(f>=0&&c.charAt(f).match(e));f++}var g=b;while(g=this.doc.getLength()-1)return 0;var c=this.doc.removeLines(a,b);return this.doc.insertLines(a+1,c),1},this.duplicateLines=function(a,b){var a=this.$clipRowToDocument(a),b=this.$clipRowToDocument(b),c=this.getLines(a,b);this.doc.insertLines(a,c);var d=b-a+1;return d},this.$clipRowToDocument=function(a){return Math.max(0,Math.min(a,this.doc.getLength()-1))},this.$clipColumnToRow=function(a,b){return b<0?0:Math.min(this.doc.getLine(a).length,b)},this.$clipPositionToDocument=function(a,b){b=Math.max(0,b);if(a<0)a=0,b=0;else{var c=this.doc.getLength();a>=c?(a=c-1,b=this.doc.getLine(c-1).length):b=Math.min(this.doc.getLine(a).length,b)}return{row:a,column:b}},this.$clipRangeToDocument=function(a){a.start.row<0?(a.start.row=0,a.start.column=0):a.start.column=this.$clipColumnToRow(a.start.row,a.start.column);var b=this.doc.getLength()-1;return a.end.row>b?(a.end.row=b,a.end.column=this.doc.getLine(b).length):a.end.column=this.$clipColumnToRow(a.end.row,a.end.column),a},this.$wrapLimit=80,this.$useWrapMode=!1,this.$wrapLimitRange={min:null,max:null},this.setUseWrapMode=function(a){if(a!=this.$useWrapMode){this.$useWrapMode=a,this.$modified=!0,this.$resetRowCache(0);if(a){var b=this.getLength();this.$wrapData=[];for(var c=0;c0?(this.$wrapLimit=b,this.$modified=!0,this.$useWrapMode&&(this.$updateWrapData(0,this.getLength()-1),this.$resetRowCache(0),this._emit("changeWrapLimit")),!0):!1},this.$constrainWrapLimit=function(a){var b=this.$wrapLimitRange.min;b&&(a=Math.max(b,a));var c=this.$wrapLimitRange.max;return c&&(a=Math.min(c,a)),Math.max(1,a)},this.getWrapLimit=function(){return this.$wrapLimit},this.getWrapLimitRange=function(){return{min:this.$wrapLimitRange.min,max:this.$wrapLimitRange.max}},this.$updateInternalDataOnChange=function(a){var b=this.$useWrapMode,c,d=a.data.action,e=a.data.range.start.row,f=a.data.range.end.row,g=a.data.range.start,h=a.data.range.end,i=null;d.indexOf("Lines")!=-1?(d=="insertLines"?f=e+a.data.lines.length:f=e,c=a.data.lines?a.data.lines.length:f-e):c=f-e;if(c!=0)if(d.indexOf("remove")!=-1){b&&this.$wrapData.splice(e,c);var j=this.$foldData;i=this.getFoldsInRange(a.data.range),this.removeFolds(i);var k=this.getFoldLine(h.row),l=0;if(k){k.addRemoveChars(h.row,h.column,g.column-h.column),k.shiftRow(-c);var m=this.getFoldLine(e);m&&m!==k&&(m.merge(k),k=m),l=j.indexOf(k)+1}for(l;l=h.row&&k.shiftRow(-c)}f=e}else{var n;if(b){n=[e,0];for(var o=0;o=e&&k.shiftRow(c)}}else{c=Math.abs(a.data.range.start.column-a.data.range.end.column),d.indexOf("remove")!=-1&&(i=this.getFoldsInRange(a.data.range),this.removeFolds(i),c=-c);var k=this.getFoldLine(e);k&&k.addRemoveChars(e,g.column,c)}return b&&this.$wrapData.length!=this.doc.getLength()&&console.error("doc.getLength() and $wrapData.length have to be the same!"),b&&this.$updateWrapData(e,f),i},this.$updateWrapData=function(a,b){var c=this.doc.getAllLines(),d=this.getTabSize(),e=this.$wrapData,g=this.$wrapLimit,h,k,l=a;b=Math.min(b,c.length-1);while(l<=b){k=this.getFoldLine(l,k);if(!k)h=this.$getDisplayTokens(f.stringTrimRight(c[l])),e[l]=this.$computeWrapSplits(h,g,d),l++;else{h=[],k.walk(function(a,b,d,e){var f;if(a){f=this.$getDisplayTokens(a,h.length),f[0]=i;for(var g=1;g=n)h.pop();e[k.start.row]=this.$computeWrapSplits(h,g,d),l=k.end.row+1}}};var b=1,c=2,i=3,j=4,l=9,n=10,o=11,p=12;this.$computeWrapSplits=function(a,b){function g(b){var d=a.slice(e,b),g=d.length;d.join("").replace(/12/g,function(){g-=1}).replace(/2/g,function(){g-=1}),f+=g,c.push(f),e=b}if(a.length==0)return[];var c=[],d=a.length,e=0,f=0;while(d-e>b){var h=e+b;if(a[h]>=n){while(a[h]>=n)h++;g(h);continue}if(a[h]==i||a[h]==j){for(h;h!=e-1;h--)if(a[h]==i)break;if(h>e){g(h);continue}h=e+b;for(h;hk&&a[h]k&&a[h]==l)h--;if(h>k){g(++h);continue}h=e+b,g(h)}return c},this.$getDisplayTokens=function(a,d){var e=[],f;d=d||0;for(var g=0;g39&&h<48||h>57&&h<64?e.push(l):h>=4352&&q(h)?e.push(b,c):e.push(b)}return e},this.$getStringScreenWidth=function(a,b,c){if(b==0)return[0,0];b==null&&(b=c+a.length*Math.max(this.getTabSize(),2)),c=c||0;var d,e;for(e=0;e=4352&&q(d)?c+=2:c+=1;if(c>b)break}return[c,e]},this.getRowLength=function(a){return!this.$useWrapMode||!this.$wrapData[a]?1:this.$wrapData[a].length+1},this.getRowHeight=function(a,b){return this.getRowLength(b)*a.lineHeight},this.getScreenLastRowColumn=function(a){var b=this.screenToDocumentPosition(a,Number.MAX_VALUE);return this.documentToScreenColumn(b.row,b.column)},this.getDocumentLastRowColumn=function(a,b){var c=this.documentToScreenRow(a,b);return this.getScreenLastRowColumn(c)},this.getDocumentLastRowColumnPosition=function(a,b){var c=this.documentToScreenRow(a,b);return this.screenToDocumentPosition(c,Number.MAX_VALUE/10)},this.getRowSplitData=function(a){return this.$useWrapMode?this.$wrapData[a]:undefined},this.getScreenTabSize=function(a){return this.$tabSize-a%this.$tabSize},this.screenToDocumentRow=function(a,b){return this.screenToDocumentPosition(a,b).row},this.screenToDocumentColumn=function(a,b){return this.screenToDocumentPosition(a,b).column},this.screenToDocumentPosition=function(a,b){if(a<0)return{row:0,column:0};var c,d=0,e=0,f,g=0,h=0,i=this.$rowCache;for(var j=0;j=a||d>=l)break;g+=h,d++,d>n&&(d=m.end.row+1,m=this.getNextFoldLine(d,m),n=m?m.start.row:Infinity),k&&i.push({docRow:d,screenRow:g})}if(m&&m.start.row<=d)c=this.getFoldDisplayLine(m),d=m.start.row;else{if(g+h<=a||d>l)return{row:l,column:this.getLine(l).length};c=this.getLine(d),m=null}if(this.$useWrapMode){var o=this.$wrapData[d];o&&(f=o[a-g],a>g&&o.length&&(e=o[a-g-1]||o[o.length-1],c=c.substring(e)))}return e+=this.$getStringScreenWidth(c,b)[1],this.$useWrapMode&&e>=f&&(e=f-1),m?m.idxToPosition(e):{row:d,column:e}},this.documentToScreenPosition=function(a,b){if(typeof b=="undefined")var c=this.$clipPositionToDocument(a.row,a.column);else c=this.$clipPositionToDocument(a,b);a=c.row,b=c.column;var d;if(this.$useWrapMode){d=this.$wrapData;if(a>d.length-1)return{row:this.getScreenLength(),column:d.length==0?0:d[d.length-1].length-1}}var e=0,f=null,g=null;g=this.getFoldAt(a,b,1),g&&(a=g.start.row,b=g.start.column);var h,i=0,j=this.$rowCache;for(var k=0;k=n){h=m.end.row+1;if(h>a)break;m=this.getNextFoldLine(h,m),n=m?m.start.row:Infinity}else h=i+1;e+=this.getRowLength(i),i=h,l&&j.push({docRow:i,screenRow:e})}var o="";m&&i>=n?(o=this.getFoldDisplayLine(m,a,b),f=m.start.row):(o=this.getLine(a).substring(0,b),f=a);if(this.$useWrapMode){var p=d[f],q=0;while(o.length>=p[q])e++,q++;o=o.substring(p[q-1]||0,o.length)}return{row:e,column:this.$getStringScreenWidth(o)[0]}},this.documentToScreenColumn=function(a,b){return this.documentToScreenPosition(a,b).column},this.documentToScreenRow=function(a,b){return this.documentToScreenPosition(a,b).row},this.getScreenLength=function(){var a=0,b=null;if(!this.$useWrapMode){a=this.getLength();var c=this.$foldData;for(var d=0;dg&&(f=b.end.row+1,b=this.$foldData[d++],g=b?b.start.row:Infinity)}return a}}).call(n.prototype),a("./edit_session/folding").Folding.call(n.prototype),a("./edit_session/bracket_match").BracketMatch.call(n.prototype),b.EditSession=n}),define("ace/config",["require","exports","module","ace/lib/lang"],function(a,b,c){function g(a){return a.replace(/-(.)/g,function(a,b){return b.toUpperCase()})}"no use strict";var d=a("./lib/lang"),e=function(){return this}(),f={packaged:!1,workerPath:"",modePath:"",themePath:"",suffix:".js"};b.get=function(a){if(!f.hasOwnProperty(a))throw new Error("Unknown confik key: "+a);return f[a]},b.set=function(a,b){if(!f.hasOwnProperty(a))throw new Error("Unknown confik key: "+a);f[a]=b},b.all=function(){return d.copyObject(f)},b.init=function(){f.packaged=a.packaged||c.packaged||e.define&&define.packaged;if(!e.document)return"";var d={},h="",i,j=document.getElementsByTagName("script");for(var k=0;kb.row||a.row==b.row&&a.column>b.column},this.getRange=function(){var a=this.selectionAnchor,b=this.selectionLead;return this.isEmpty()?g.fromPoints(b,b):this.isBackwards()?g.fromPoints(b,a):g.fromPoints(a,b)},this.clearSelection=function(){this.$isEmpty||(this.$isEmpty=!0,this._emit("changeSelection"))},this.selectAll=function(){var a=this.doc.getLength()-1;this.setSelectionAnchor(a,this.doc.getLine(a).length),this.moveCursorTo(0,0)},this.setSelectionRange=function(a,b){b?(this.setSelectionAnchor(a.end.row,a.end.column),this.selectTo(a.start.row,a.start.column)):(this.setSelectionAnchor(a.start.row,a.start.column),this.selectTo(a.end.row,a.end.column)),this.$desiredColumn=null},this.$moveSelection=function(a){var b=this.selectionLead;this.$isEmpty&&this.setSelectionAnchor(b.row,b.column),a.call(this)},this.selectTo=function(a,b){this.$moveSelection(function(){this.moveCursorTo(a,b)})},this.selectToPosition=function(a){this.$moveSelection(function(){this.moveCursorToPosition(a)})},this.selectUp=function(){this.$moveSelection(this.moveCursorUp)},this.selectDown=function(){this.$moveSelection(this.moveCursorDown)},this.selectRight=function(){this.$moveSelection(this.moveCursorRight)},this.selectLeft=function(){this.$moveSelection(this.moveCursorLeft)},this.selectLineStart=function(){this.$moveSelection(this.moveCursorLineStart)},this.selectLineEnd=function(){this.$moveSelection(this.moveCursorLineEnd)},this.selectFileEnd=function(){this.$moveSelection(this.moveCursorFileEnd)},this.selectFileStart=function(){this.$moveSelection(this.moveCursorFileStart)},this.selectWordRight=function(){this.$moveSelection(this.moveCursorWordRight)},this.selectWordLeft=function(){this.$moveSelection(this.moveCursorWordLeft)},this.selectWord=function(){var a=this.getCursor(),b=this.session.getWordRange(a.row,a.column);this.setSelectionRange(b)},this.selectAWord=function(){var a=this.getCursor(),b=this.session.getAWordRange(a.row,a.column);this.setSelectionRange(b)},this.selectLine=function(){var a=this.selectionLead.row,b,c=this.session.getFoldLine(a);c?(a=c.start.row,b=c.end.row):b=a,this.setSelectionAnchor(a,0),this.$moveSelection(function(){this.moveCursorTo(b+1,0)})},this.moveCursorUp=function(){this.moveCursorBy(-1,0)},this.moveCursorDown=function(){this.moveCursorBy(1,0)},this.moveCursorLeft=function(){var a=this.selectionLead.getPosition(),b;if(b=this.session.getFoldAt(a.row,a.column,-1))this.moveCursorTo(b.start.row,b.start.column);else if(a.column==0)a.row>0&&this.moveCursorTo(a.row-1,this.doc.getLine(a.row-1).length);else{var c=this.session.getTabSize();this.session.isTabStop(a)&&this.doc.getLine(a.row).slice(a.column-c,a.column).split(" ").length-1==c?this.moveCursorBy(0,-c):this.moveCursorBy(0,-1)}},this.moveCursorRight=function(){var a=this.selectionLead.getPosition(),b;if(b=this.session.getFoldAt(a.row,a.column,1))this.moveCursorTo(b.end.row,b.end.column);else if(this.selectionLead.column==this.doc.getLine(this.selectionLead.row).length)this.selectionLead.row=c.length){this.moveCursorTo(a,c.length),this.moveCursorRight(),a0&&this.moveCursorWordLeft();return}if(g=this.session.tokenRe.exec(f))b-=this.session.tokenRe.lastIndex,this.session.tokenRe.lastIndex=0;this.moveCursorTo(a,b)},this.moveCursorBy=function(a,b){var c=this.session.documentToScreenPosition(this.selectionLead.row,this.selectionLead.column);b===0&&(this.$desiredColumn?c.column=this.$desiredColumn:this.$desiredColumn=c.column);var d=this.session.screenToDocumentPosition(c.row+a,c.column);this.moveCursorTo(d.row,d.column+b,b===0)},this.moveCursorToPosition=function(a){this.moveCursorTo(a.row,a.column)},this.moveCursorTo=function(a,b,c){var d=this.session.getFoldAt(a,b,1);d&&(a=d.start.row,b=d.start.column),this.$keepDesiredColumnOnChange=!0,this.selectionLead.setPosition(a,b),this.$keepDesiredColumnOnChange=!1,c||(this.$desiredColumn=null)},this.moveCursorToScreen=function(a,b,c){var d=this.session.screenToDocumentPosition(a,b);this.moveCursorTo(d.row,d.column,c)},this.detach=function(){this.selectionLead.detach(),this.selectionAnchor.detach(),this.session=this.doc=null},this.fromOrientedRange=function(a){this.setSelectionRange(a,a.cursor==a.start),this.$desiredColumn=a.desiredColumn||this.$desiredColumn},this.toOrientedRange=function(a){var b=this.getRange();return a?(a.start.column=b.start.column,a.start.row=b.start.row,a.end.column=b.end.column,a.end.row=b.end.row):a=b,a.cursor=this.isBackwards()?a.start:a.end,a.desiredColumn=this.$desiredColumn,a}}).call(h.prototype),b.Selection=h}),define("ace/range",["require","exports","module"],function(a,b,c){"use strict";var d=function(a,b,c,d){this.start={row:a,column:b},this.end={row:c,column:d}};(function(){this.isEqual=function(a){return this.start.row==a.start.row&&this.end.row==a.end.row&&this.start.column==a.start.column&&this.end.column==a.end.column},this.toString=function(){return"Range: ["+this.start.row+"/"+this.start.column+"] -> ["+this.end.row+"/"+this.end.column+"]"},this.contains=function(a,b){return this.compare(a,b)==0},this.compareRange=function(a){var b,c=a.end,d=a.start;return b=this.compare(c.row,c.column),b==1?(b=this.compare(d.row,d.column),b==1?2:b==0?1:0):b==-1?-2:(b=this.compare(d.row,d.column),b==-1?-1:b==1?42:0)},this.comparePoint=function(a){return this.compare(a.row,a.column)},this.containsRange=function(a){return this.comparePoint(a.start)==0&&this.comparePoint(a.end)==0},this.intersectsRange=function(a){var b=this.compareRange(a);return b==-1||b==0||b==1},this.isEnd=function(a,b){return this.end.row==a&&this.end.column==b},this.isStart=function(a,b){return this.start.row==a&&this.start.column==b},this.setStart=function(a,b){typeof a=="object"?(this.start.column=a.column,this.start.row=a.row):(this.start.row=a,this.start.column=b)},this.setEnd=function(a,b){typeof a=="object"?(this.end.column=a.column,this.end.row=a.row):(this.end.row=a,this.end.column=b)},this.inside=function(a,b){return this.compare(a,b)==0?this.isEnd(a,b)||this.isStart(a,b)?!1:!0:!1},this.insideStart=function(a,b){return this.compare(a,b)==0?this.isEnd(a,b)?!1:!0:!1},this.insideEnd=function(a,b){return this.compare(a,b)==0?this.isStart(a,b)?!1:!0:!1},this.compare=function(a,b){return!this.isMultiLine()&&a===this.start.row?bthis.end.column?1:0:athis.end.row?1:this.start.row===a?b>=this.start.column?0:-1:this.end.row===a?b<=this.end.column?0:1:0},this.compareStart=function(a,b){return this.start.row==a&&this.start.column==b?-1:this.compare(a,b)},this.compareEnd=function(a,b){return this.end.row==a&&this.end.column==b?1:this.compare(a,b)},this.compareInside=function(a,b){return this.end.row==a&&this.end.column==b?1:this.start.row==a&&this.start.column==b?-1:this.compare(a,b)},this.clipRows=function(a,b){if(this.end.row>b)var c={row:b+1,column:0};if(this.start.row>b)var e={row:b+1,column:0};if(this.start.row=0&&/^[\w\d]/.test(h)||e<=g&&/[\w\d]$/.test(h))return;h=f.substring(c.start.column,c.end.column);if(!/^[\w\d]+$/.test(h))return;var i=a.getCursorPosition(),j={wrap:!0,wholeWord:!0,caseSensitive:!0,needle:h},k=a.$search.getOptions();a.$search.set(j);var l=a.$search.findAll(b);l.forEach(function(a){if(!a.contains(i.row,i.column)){var c=b.addMarker(a,"ace_selected_word","text");b.$selectionOccurrences.push(c)}}),a.$search.set(k)},this.clearSelectionHighlight=function(a){if(!a.session.$selectionOccurrences)return;a.session.$selectionOccurrences.forEach(function(b){a.session.removeMarker(b)}),a.session.$selectionOccurrences=[]},this.createModeDelegates=function(a){if(!this.$embeds)return;this.$modes={};for(var b=0;b1&&e[i].token.length!==j-1)throw new Error("Matching groups and length of the token array don't match in rule #"+i+" of state "+c);h[g]={rule:i,len:j},g+=j,f.push(k)}this.regExps[c]=new RegExp("(?:("+f.join(")|(")+")|(.))",b)}};(function(){this.getLineTokens=function(a,b){var c=b,d=this.rules[c],e=this.matchMappings[c],f=this.regExps[c];f.lastIndex=0;var g,h=[],i=0,j={type:null,value:""};while(g=f.exec(a)){var k="text",l=null,m=[g[0]];for(var n=0;n1&&(m=g.slice(n+2,n+1+e[n].len)),typeof l.token=="function"?k=l.token.apply(this,m):k=l.token;var o=l.next;o&&o!==c&&(c=o,d=this.rules[c],e=this.matchMappings[c],i=f.lastIndex,f=this.regExps[c],f.lastIndex=i);break}if(m[0]){typeof k=="string"&&(m=[m.join("")],k=[k]);for(var n=0;n=b&&(a.row=Math.max(0,b-1),a.column=this.getLine(b-1).length),a},this.insert=function(a,b){if(!b||b.length===0)return a;a=this.$clipPosition(a),this.getLength()<=1&&this.$detectNewLine(b);var c=this.$split(b),d=c.splice(0,1)[0],e=c.length==0?null:c.splice(c.length-1,1)[0];return a=this.insertInLine(a,d),e!==null&&(a=this.insertNewLine(a),a=this.insertLines(a.row,c),a=this.insertInLine(a,e||"")),a},this.insertLines=function(a,b){if(b.length==0)return{row:a,column:0};var c=[a,0];c.push.apply(c,b),this.$lines.splice.apply(this.$lines,c);var d=new f(a,0,a+b.length,0),e={action:"insertLines",range:d,lines:b};return this._emit("change",{data:e}),d.end},this.insertNewLine=function(a){a=this.$clipPosition(a);var b=this.$lines[a.row]||"";this.$lines[a.row]=b.substring(0,a.column),this.$lines.splice(a.row+1,0,b.substring(a.column,b.length));var c={row:a.row+1,column:0},d={action:"insertText",range:f.fromPoints(a,c),text:this.getNewLineCharacter()};return this._emit("change",{data:d}),c},this.insertInLine=function(a,b){if(b.length==0)return a;var c=this.$lines[a.row]||"";this.$lines[a.row]=c.substring(0,a.column)+b+c.substring(a.column);var d={row:a.row,column:a.column+b.length},e={action:"insertText",range:f.fromPoints(a,d),text:b};return this._emit("change",{data:e}),d},this.remove=function(a){a.start=this.$clipPosition(a.start),a.end=this.$clipPosition(a.end);if(a.isEmpty())return a.start;var b=a.start.row,c=a.end.row;if(a.isMultiLine()){var d=a.start.column==0?b:b+1,e=c-1;a.end.column>0&&this.removeInLine(c,0,a.end.column),e>=d&&this.removeLines(d,e),d!=b&&(this.removeInLine(b,a.start.column,this.getLine(b).length),this.removeNewLine(a.start.row))}else this.removeInLine(b,a.start.column,a.end.column);return a.start},this.removeInLine=function(a,b,c){if(b==c)return;var d=new f(a,b,a,c),e=this.getLine(a),g=e.substring(b,c),h=e.substring(0,b)+e.substring(c,e.length);this.$lines.splice(a,1,h);var i={action:"removeText",range:d,text:g};return this._emit("change",{data:i}),d.start},this.removeLines=function(a,b){var c=new f(a,0,b+1,0),d=this.$lines.splice(a,b-a+1),e={action:"removeLines",range:c,nl:this.getNewLineCharacter(),lines:d};return this._emit("change",{data:e}),d},this.removeNewLine=function(a){var b=this.getLine(a),c=this.getLine(a+1),d=new f(a,b.length,a+1,0),e=b+c;this.$lines.splice(a,2,e);var g={action:"removeText",range:d,text:this.getNewLineCharacter()};this._emit("change",{data:g})},this.replace=function(a,b){if(b.length==0&&a.isEmpty())return a.start;if(b==this.getTextRange(a))return a.end;this.remove(a);if(b)var c=this.insert(a.start,b);else c=a.start;return c},this.applyDeltas=function(a){for(var b=0;b=0;b--){var c=a[b],d=f.fromPoints(c.range.start,c.range.end);c.action=="insertLines"?this.removeLines(d.start.row,d.end.row-1):c.action=="insertText"?this.remove(d):c.action=="removeLines"?this.insertLines(d.start.row,c.lines):c.action=="removeText"&&this.insert(d.start,c.text)}}}).call(h.prototype),b.Document=h}),define("ace/anchor",["require","exports","module","ace/lib/oop","ace/lib/event_emitter"],function(a,b,c){"use strict";var d=a("./lib/oop"),e=a("./lib/event_emitter").EventEmitter,f=b.Anchor=function(a,b,c){this.document=a,typeof c=="undefined"?this.setPosition(b.row,b.column):this.setPosition(b,c),this.$onChange=this.onChange.bind(this),a.on("change",this.$onChange)};(function(){d.implement(this,e),this.getPosition=function(){return this.$clipPositionToDocument(this.row,this.column)},this.getDocument=function(){return this.document},this.onChange=function(a){var b=a.data,c=b.range;if(c.start.row==c.end.row&&c.start.row!=this.row)return;if(c.start.row>this.row)return;if(c.start.row==this.row&&c.start.column>this.column)return;var d=this.row,e=this.column;b.action==="insertText"?c.start.row===d&&c.start.column<=e?c.start.row===c.end.row?e+=c.end.column-c.start.column:(e-=c.start.column,d+=c.end.row-c.start.row):c.start.row!==c.end.row&&c.start.row=e?e=c.start.column:e=Math.max(0,e-(c.end.column-c.start.column)):c.start.row!==c.end.row&&c.start.row=this.document.getLength()?(c.row=Math.max(0,this.document.getLength()-1),c.column=this.document.getLine(c.row).length):a<0?(c.row=0,c.column=0):(c.row=a,c.column=Math.min(this.document.getLine(c.row).length,Math.max(0,b))),b<0&&(c.column=0),c}}).call(f.prototype)}),define("ace/background_tokenizer",["require","exports","module","ace/lib/oop","ace/lib/event_emitter"],function(a,b,c){"use strict";var d=a("./lib/oop"),e=a("./lib/event_emitter").EventEmitter,f=function(a,b){this.running=!1,this.lines=[],this.currentLine=0,this.tokenizer=a;var c=this;this.$worker=function(){if(!c.running)return;var a=new Date,b=c.currentLine,d=c.doc,e=0,f=d.getLength();while(c.currentLine20){c.fireUpdateEvent(b,c.currentLine-1),c.running=setTimeout(c.$worker,20);return}}c.running=!1,c.fireUpdateEvent(b,f-1)}};(function(){d.implement(this,e),this.setTokenizer=function(a){this.tokenizer=a,this.lines=[],this.start(0)},this.setDocument=function(a){this.doc=a,this.lines=[],this.stop()},this.fireUpdateEvent=function(a,b){var c={first:a,last:b};this._emit("update",{data:c})},this.start=function(a){this.currentLine=Math.min(a||0,this.currentLine,this.doc.getLength()),this.lines.splice(this.currentLine,this.lines.length),this.stop(),this.running=setTimeout(this.$worker,700)},this.stop=function(){this.running&&clearTimeout(this.running),this.running=!1},this.getTokens=function(a,b){return this.$tokenizeRows(a,b)},this.getState=function(a){return this.$tokenizeRows(a,a)[0].state},this.$tokenizeRows=function(a,b){if(!this.doc||isNaN(a)||isNaN(b))return[{state:"start",tokens:[]}];var c=[],d="start",e=!1;a>0&&this.lines[a-1]?(d=this.lines[a-1].state,e=!0):a==0?(d="start",e=!0):this.lines.length>0&&(d=this.lines[this.lines.length-1].state);var f=this.doc.getLines(a,b);for(var g=a;g<=b;g++)if(!this.lines[g]){var h=this.tokenizer.getLineTokens(f[g-a]||"",d),d=h.state;c.push(h),e&&(this.lines[g]=h)}else{var h=this.lines[g];d=h.state,c.push(h)}return c}}).call(f.prototype),b.BackgroundTokenizer=f}),define("ace/edit_session/folding",["require","exports","module","ace/range","ace/edit_session/fold_line","ace/edit_session/fold","ace/token_iterator"],function(a,b,c){function h(){this.getFoldAt=function(a,b,c){var d=this.getFoldLine(a);if(!d)return null;var e=d.folds;for(var f=0;f=a)return e;if(e.end.row>a)return null}return null},this.getNextFoldLine=function(a,b){var c=this.$foldData,d=0;b&&(d=c.indexOf(b)),d==-1&&(d=0);for(d;d=a)return e}return null},this.getFoldedRowCount=function(a,b){var c=this.$foldData,d=b-a+1;for(var e=0;e=b){h=a?d-=b-h:d=0);break}g>=a&&(h>=a?d-=g-h:d-=g-a+1)}return d},this.$addFoldLine=function(a){return this.$foldData.push(a),this.$foldData.sort(function(a,b){return a.start.row-b.start.row}),a},this.addFold=function(a,b){var c=this.$foldData,d=!1,g;a instanceof f?g=a:g=new f(b,a),this.$clipRangeToDocument(g.range);var h=g.start.row,i=g.start.column,j=g.end.row,k=g.end.column;if(g.placeholder.length<2)throw"Placeholder has to be at least 2 characters";if(h==j&&k-i<2)throw"The range has to be at least 2 characters width";var l=this.getFoldAt(h,i,1),m=this.getFoldAt(j,k,-1);if(l&&m==l)return l.addSubFold(g);if(l&&!l.range.isStart(h,i)||m&&!m.range.isEnd(j,k))throw"A fold can't intersect already existing fold"+g.range+l.range;var n=this.getFoldsInRange(g.range);n.length>0&&(this.removeFolds(n),g.subFolds=n);for(var o=0;othis.endRow)throw"Can't add a fold to this FoldLine as it has no connection";this.folds.push(a),this.folds.sort(function(a,b){return-a.range.compareEnd(b.start.row,b.start.column)}),this.range.compareEnd(a.start.row,a.start.column)>0?(this.end.row=a.end.row,this.end.column=a.end.column):this.range.compareStart(a.end.row,a.end.column)<0&&(this.start.row=a.start.row,this.start.column=a.start.column)}else if(a.start.row==this.end.row)this.folds.push(a),this.end.row=a.end.row,this.end.column=a.end.column;else{if(a.end.row!=this.start.row)throw"Trying to add fold to FoldRow that doesn't have a matching row";this.folds.unshift(a),this.start.row=a.start.row,this.start.column=a.start.column}a.foldLine=this},this.containsRow=function(a){return a>=this.start.row&&a<=this.end.row},this.walk=function(a,b,c){var d=0,e=this.folds,f,g,h,i=!0;b==null&&(b=this.end.row,c=this.end.column);for(var j=0;j=this.$rowTokens.length){this.$row+=1;if(this.$row>=a)return this.$row=a-1,null;this.$rowTokens=this.$session.getTokens(this.$row,this.$row)[0].tokens,this.$tokenIndex=0}return this.$rowTokens[this.$tokenIndex]},this.getCurrentToken=function(){return this.$rowTokens[this.$tokenIndex]},this.getCurrentTokenRow=function(){return this.$row},this.getCurrentTokenColumn=function(){var a=this.$rowTokens,b=this.$tokenIndex,c=a[b].start;if(c!==undefined)return c;c=0;while(b>0)b-=1,c+=a[b].value.length;return c}}).call(d.prototype),b.TokenIterator=d}),define("ace/edit_session/bracket_match",["require","exports","module","ace/token_iterator"],function(a,b,c){function e(){this.findMatchingBracket=function(a){if(a.column==0)return null;var b=this.getLine(a.row).charAt(a.column-1);if(b=="")return null;var c=b.match(/([\(\[\{])|([\)\]\}])/);return c?c[1]?this.$findClosingBracket(c[1],a):this.$findOpeningBracket(c[2],a):null},this.$brackets={")":"(","(":")","]":"[","[":"]","{":"}","}":"{"},this.$findOpeningBracket=function(a,b){var c=this.$brackets[a],e=1,f=new d(this,b.row,b.column),g=f.getCurrentToken();if(!g)return null;var h=new RegExp("(\\.?"+g.type.replace(".","|").replace("rparen","lparen|rparen")+")+"),i=b.column-f.getCurrentTokenColumn()-2,j=g.value;for(;;){while(i>=0){var k=j.charAt(i);if(k==c){e-=1;if(e==0)return{row:f.getCurrentTokenRow(),column:i+f.getCurrentTokenColumn()}}else k==a&&(e+=1);i-=1}do g=f.stepBackward();while(g&&!h.test(g.type));if(g==null)break;j=g.value,i=j.length-1}return null},this.$findClosingBracket=function(a,b){var c=this.$brackets[a],e=1,f=new d(this,b.row,b.column),g=f.getCurrentToken();if(!g)return null;var h=new RegExp("(\\.?"+g.type.replace(".","|").replace("lparen","lparen|rparen")+")+"),i=b.column-f.getCurrentTokenColumn();for(;;){var j=g.value,k=j.length;while(i=0;h--){var i=g[h],j=c.$rangeFromMatch(f,i.offset,i.str.length);if(d(j))return!0}})}}},this.$rangeFromMatch=function(a,b,c){return new f(a,b,a,b+c)},this.$assembleRegExp=function(){if(this.$options.regExp)var a=this.$options.needle;else a=d.escapeRegExp(this.$options.needle);this.$options.wholeWord&&(a="\\b"+a+"\\b");var b="g";this.$options.caseSensitive||(b+="i");var c=new RegExp(a,b);return c},this.$forwardLineIterator=function(a){function k(e){var f=a.getLine(e);return b&&e==c.end.row&&(f=f.substring(0,c.end.column)),j&&e==d.row&&(f=f.substring(0,d.column)),f}var b=this.$options.scope==g.SELECTION,c=this.$options.range||a.getSelection().getRange(),d=this.$options.start||c[b?"start":"end"],e=b?c.start.row:0,f=b?c.start.column:0,h=b?c.end.row:a.getLength()-1,i=this.$options.wrap,j=!1;return{forEach:function(a){var b=d.row,c=k(b),g=d.column,l=!1;j=!1;while(!a(c,g,b)){if(l)return;b++,g=0;if(b>h){if(!i)return;b=e,g=f,j=!0}b==d.row&&(l=!0),c=k(b)}}}},this.$backwardLineIterator=function(a){var b=this.$options.scope==g.SELECTION,c=this.$options.range||a.getSelection().getRange(),d=this.$options.start||c[b?"end":"start"],e=b?c.start.row:0,f=b?c.start.column:0,h=b?c.end.row:a.getLength()-1,i=this.$options.wrap;return{forEach:function(g){var j=d.row,k=a.getLine(j).substring(0,d.column),l=0,m=!1,n=!1;while(!g(k,l,j)){if(m)return;j--,l=0;if(j0},this.hasRedo=function(){return this.$redoStack.length>0}}).call(d.prototype),b.UndoManager=d}),define("ace/virtual_renderer",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/event","ace/lib/useragent","ace/config","ace/lib/net","ace/layer/gutter","ace/layer/marker","ace/layer/text","ace/layer/cursor","ace/scrollbar","ace/renderloop","ace/lib/event_emitter","text!ace/css/editor.css"],function(a,b,c){"use strict";var d=a("./lib/oop"),e=a("./lib/dom"),f=a("./lib/event"),g=a("./lib/useragent"),h=a("./config"),i=a("./lib/net"),j=a("./layer/gutter").Gutter,k=a("./layer/marker").Marker,l=a("./layer/text").Text,m=a("./layer/cursor").Cursor,n=a("./scrollbar").ScrollBar,o=a("./renderloop").RenderLoop,p=a("./lib/event_emitter").EventEmitter,q=a("text!./css/editor.css");e.importCssString(q,"ace_editor");var r=function(a,b){var c=this;this.container=a,e.addCssClass(a,"ace_editor"),this.setTheme(b),this.$gutter=e.createElement("div"),this.$gutter.className="ace_gutter",this.container.appendChild(this.$gutter),this.scroller=e.createElement("div"),this.scroller.className="ace_scroller",this.container.appendChild(this.scroller),this.content=e.createElement("div"),this.content.className="ace_content",this.scroller.appendChild(this.content),this.$gutterLayer=new j(this.$gutter),this.$gutterLayer.on("changeGutterWidth",this.onResize.bind(this,!0)),this.$markerBack=new k(this.content);var d=this.$textLayer=new l(this.content);this.canvas=d.element,this.$markerFront=new k(this.content),this.characterWidth=d.getCharacterWidth(),this.lineHeight=d.getLineHeight(),this.$cursorLayer=new m(this.content),this.$cursorPadding=8,this.$horizScroll=!0,this.$horizScrollAlwaysVisible=!0,this.$animatedScroll=!1,this.scrollBar=new n(a),this.scrollBar.addEventListener("scroll",function(a){c.session.setScrollTop(a.data)}),this.scrollTop=0,this.scrollLeft=0,f.addListener(this.scroller,"scroll",function(){var a=c.scroller.scrollLeft;c.scrollLeft=a,c.session.setScrollLeft(a),a==0?c.$gutter.className="ace_gutter":c.$gutter.className="ace_gutter horscroll"}),this.cursorPos={row:0,column:0},this.$textLayer.addEventListener("changeCharacterSize",function(){c.characterWidth=d.getCharacterWidth(),c.lineHeight=d.getLineHeight(),c.$updatePrintMargin(),c.onResize(!0),c.$loop.schedule(c.CHANGE_FULL)}),this.$size={width:0,height:0,scrollerHeight:0,scrollerWidth:0},this.layerConfig={width:1,padding:0,firstRow:0,firstRowScreen:0,lastRow:0,lineHeight:1,characterWidth:1,minHeight:1,maxHeight:1,offset:0,height:1},this.$loop=new o(this.$renderChanges.bind(this),this.container.ownerDocument.defaultView),this.$loop.schedule(this.CHANGE_FULL),this.setPadding(4),this.$updatePrintMargin()};(function(){this.showGutter=!0,this.CHANGE_CURSOR=1,this.CHANGE_MARKER=2,this.CHANGE_GUTTER=4,this.CHANGE_SCROLL=8,this.CHANGE_LINES=16,this.CHANGE_TEXT=32,this.CHANGE_SIZE=64,this.CHANGE_MARKER_BACK=128,this.CHANGE_MARKER_FRONT=256,this.CHANGE_FULL=512,this.CHANGE_H_SCROLL=1024,d.implement(this,p),this.setSession=function(a){this.session=a,this.$cursorLayer.setSession(a),this.$markerBack.setSession(a),this.$markerFront.setSession(a),this.$gutterLayer.setSession(a),this.$textLayer.setSession(a),this.$loop.schedule(this.CHANGE_FULL)},this.updateLines=function(a,b){b===undefined&&(b=Infinity),this.$changedLines?(this.$changedLines.firstRow>a&&(this.$changedLines.firstRow=a),this.$changedLines.lastRowc.lastRow+1)return;if(bd&&this.session.setScrollTop(d),this.scrollTop+this.$size.scrollerHeightc&&(c0)return!0;if(b>0&&this.session.getScrollTop()+this.$size.scrollerHeighth&&(e=g.end.row+1,g=this.session.getNextFoldLine(e,g),h=g?g.start.row:Infinity);if(e>f)break;var j=this.$annotations[e]||b;c.push("
    ",e+1);if(i){var k=i[e];k==null&&(k=i[e]=this.session.getFoldWidget(e)),k&&c.push("")}var l=this.session.getRowLength(e)-1;while(l--)c.push("
    ¦");c.push("
    "),e++}this.element=d.setInnerHtml(this.element,c.join("")),this.element.style.height=a.minHeight+"px";var m=this.element.offsetWidth;m!==this.gutterWidth&&(this.gutterWidth=m,this._emit("changeGutterWidth",m))},this.$showFoldWidgets=!0,this.setShowFoldWidgets=function(a){a?d.addCssClass(this.element,"ace_folding-enabled"):d.removeCssClass(this.element,"ace_folding-enabled"),this.$showFoldWidgets=a},this.getShowFoldWidgets=function(){return this.$showFoldWidgets}}).call(g.prototype),b.Gutter=g}),define("ace/layer/marker",["require","exports","module","ace/range","ace/lib/dom"],function(a,b,c){"use strict";var d=a("../range").Range,e=a("../lib/dom"),f=function(a){this.element=e.createElement("div"),this.element.className="ace_layer ace_marker-layer",a.appendChild(this.element)};(function(){this.$padding=0,this.setPadding=function(a){this.$padding=a},this.setSession=function(a){this.session=a},this.setMarkers=function(a){this.markers=a},this.update=function(a){var a=a||this.config;if(!a)return;this.config=a;var b=[];for(var c in this.markers){var d=this.markers[c],f=d.range.clipRows(a.firstRow,a.lastRow);if(f.isEmpty())continue;f=f.toScreenRange(this.session);if(d.renderer){var g=this.$getTop(f.start.row,a),h=Math.round(this.$padding+f.start.column*a.characterWidth);d.renderer(b,f,h,g,a)}else f.isMultiLine()?d.type=="text"?this.drawTextMarker(b,f,d.clazz,a):this.drawMultiLineMarker(b,f,d.clazz,a,d.type):this.drawSingleLineMarker(b,f,d.clazz,a,null,d.type)}this.element=e.setInnerHtml(this.element,b.join(""))},this.$getTop=function(a,b){return(a-b.firstRowScreen)*b.lineHeight},this.drawTextMarker=function(a,b,c,e){var f=b.start.row,g=new d(f,b.start.column,f,this.session.getScreenLastRowColumn(f));this.drawSingleLineMarker(a,g,c,e,1,"text"),f=b.end.row,g=new d(f,0,f,b.end.column),this.drawSingleLineMarker(a,g,c,e,0,"text");for(f=b.start.row+1;f"),j=this.$getTop(b.end.row,d),i=Math.round(b.end.column*d.characterWidth),a.push("
    "),h=(b.end.row-b.start.row-1)*d.lineHeight;if(h<0)return;j=this.$getTop(b.start.row+1,d),a.push("
    ")},this.drawSingleLineMarker=function(a,b,c,d,e,f){var g=f==="background"?0:this.$padding,h=d.lineHeight;if(f==="background")var i=d.width;else i=Math.round((b.end.column+(e||0)-b.start.column)*d.characterWidth);var j=this.$getTop(b.start.row,d),k=Math.round(g+b.start.column*d.characterWidth);a.push("
    ")}}).call(f.prototype),b.Marker=f}),define("ace/layer/text",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/lang","ace/lib/useragent","ace/lib/event_emitter"],function(a,b,c){"use strict";var d=a("../lib/oop"),e=a("../lib/dom"),f=a("../lib/lang"),g=a("../lib/useragent"),h=a("../lib/event_emitter").EventEmitter,i=function(a){this.element=e.createElement("div"),this.element.className="ace_layer ace_text-layer",a.appendChild(this.element),this.$characterSize=this.$measureSizes()||{width:0,height:0},this.$pollSizeChanges()};(function(){d.implement(this,h),this.EOF_CHAR="¶",this.EOL_CHAR="¬",this.TAB_CHAR="→",this.SPACE_CHAR="·",this.$padding=0,this.setPadding=function(a){this.$padding=a,this.element.style.padding="0 "+a+"px"},this.getLineHeight=function(){return this.$characterSize.height||1},this.getCharacterWidth=function(){return this.$characterSize.width||1},this.checkForSizeChanges=function(){var a=this.$measureSizes();a&&(this.$characterSize.width!==a.width||this.$characterSize.height!==a.height)&&(this.$characterSize=a,this._emit("changeCharacterSize",{data:a}))},this.$pollSizeChanges=function(){var a=this;this.$pollSizeChangesTimer=setInterval(function(){a.checkForSizeChanges()},500)},this.$fontStyles={fontFamily:1,fontSize:1,fontWeight:1,fontStyle:1,lineHeight:1},this.$measureSizes=g.isIE||g.isOldGecko?function(){var a=1e3;if(!this.$measureNode){var b=this.$measureNode=e.createElement("div"),c=b.style;c.width=c.height="auto",c.left=c.top=-a*40+"px",c.visibility="hidden",c.position="fixed",c.overflow="visible",c.whiteSpace="nowrap",b.innerHTML=f.stringRepeat("Xy",a);if(this.element.ownerDocument.body)this.element.ownerDocument.body.appendChild(b);else{var d=this.element.parentNode;while(!e.hasCssClass(d,"ace_editor"))d=d.parentNode;d.appendChild(b)}}if(!this.element.offsetWidth)return null;var c=this.$measureNode.style,g=e.computedStyle(this.element);for(var h in this.$fontStyles)c[h]=g[h];var i={height:this.$measureNode.offsetHeight,width:this.$measureNode.offsetWidth/(a*2)};return i.width==0||i.height==0?null:i}:function(){if(!this.$measureNode){var a=this.$measureNode=e.createElement("div"),b=a.style;b.width=b.height="auto",b.left=b.top="-100px",b.visibility="hidden",b.position="fixed",b.overflow="visible",b.whiteSpace="nowrap",a.innerHTML="X";var c=this.element.parentNode;while(c&&!e.hasCssClass(c,"ace_editor"))c=c.parentNode;if(!c)return this.$measureNode=null;c.appendChild(a)}var d=this.$measureNode.getBoundingClientRect(),f={height:d.height,width:d.width};return f.width==0||f.height==0?null:f},this.setSession=function(a){this.session=a},this.showInvisibles=!1,this.setShowInvisibles=function(a){return this.showInvisibles==a?!1:(this.showInvisibles=a,!0)},this.$tabStrings=[],this.$computeTabString=function(){var a=this.session.getTabSize(),b=this.$tabStrings=[0];for(var c=1;c"+this.TAB_CHAR+(new Array(c)).join(" ")+""):b.push((new Array(c+1)).join(" "))},this.updateLines=function(a,b,c){this.$computeTabString(),(this.config.lastRow!=a.lastRow||this.config.firstRow!=a.firstRow)&&this.scrollLines(a),this.config=a;var d=Math.max(b,a.firstRow),f=Math.min(c,a.lastRow),g=this.element.childNodes,h=0;for(var i=a.firstRow;i0;d--)c.removeChild(c.firstChild);if(b.lastRow>a.lastRow)for(var d=this.session.getFoldedRowCount(a.lastRow+1,b.lastRow);d>0;d--)c.removeChild(c.lastChild);if(a.firstRowb.lastRow){var e=this.$renderLinesFragment(a,b.lastRow+1,a.lastRow);c.appendChild(e)}},this.$renderLinesFragment=function(a,b,c){var d=this.element.ownerDocument.createDocumentFragment(),f=b,g=this.session.getNextFoldLine(f),h=g?g.start.row:Infinity;for(;;){f>h&&(f=g.end.row+1,g=this.session.getNextFoldLine(f,g),h=g?g.start.row:Infinity);if(f>c)break;var i=e.createElement("div"),j=[],k=this.session.getTokens(f,f);k.length==1&&this.$renderLine(j,f,k[0].tokens,!1),i.innerHTML=j.join("");if(this.$useLineGroups())i.className="ace_line_group",d.appendChild(i);else{var l=i.childNodes;while(l.length)d.appendChild(l[0])}f++}return d},this.update=function(a){this.$computeTabString(),this.config=a;var b=[],c=a.firstRow,d=a.lastRow,f=c,g=this.session.getNextFoldLine(f),h=g?g.start.row:Infinity;for(;;){f>h&&(f=g.end.row+1,g=this.session.getNextFoldLine(f,g),h=g?g.start.row:Infinity);if(f>d)break;this.$useLineGroups()&&b.push("
    ");var i=this.session.getTokens(f,f);i.length==1&&this.$renderLine(b,f,i[0].tokens,!1),this.$useLineGroups()&&b.push("
    "),f++}this.element=e.setInnerHtml(this.element,b.join(""))},this.$textToken={text:!0,rparen:!0,lparen:!0},this.$renderToken=function(a,b,c,d){var e=this,f=/\t|&|<|( +)|([\v\f \u00a0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000])|[\u1100-\u115F]|[\u11A3-\u11A7]|[\u11FA-\u11FF]|[\u2329-\u232A]|[\u2E80-\u2E99]|[\u2E9B-\u2EF3]|[\u2F00-\u2FD5]|[\u2FF0-\u2FFB]|[\u3000-\u303E]|[\u3041-\u3096]|[\u3099-\u30FF]|[\u3105-\u312D]|[\u3131-\u318E]|[\u3190-\u31BA]|[\u31C0-\u31E3]|[\u31F0-\u321E]|[\u3220-\u3247]|[\u3250-\u32FE]|[\u3300-\u4DBF]|[\u4E00-\uA48C]|[\uA490-\uA4C6]|[\uA960-\uA97C]|[\uAC00-\uD7A3]|[\uD7B0-\uD7C6]|[\uD7CB-\uD7FB]|[\uF900-\uFAFF]|[\uFE10-\uFE19]|[\uFE30-\uFE52]|[\uFE54-\uFE66]|[\uFE68-\uFE6B]|[\uFF01-\uFF60]|[\uFFE0-\uFFE6]/g,h=function(a,c,d,f,h){if(a.charCodeAt(0)==32)return(new Array(a.length+1)).join(" ");if(a==" "){var i=e.session.getScreenTabSize(b+f);return b+=i-1,e.$tabStrings[i]}if(a=="&")return g.isOldGecko?"&":"&";if(a=="<")return"<";if(a==" "){var j=e.showInvisibles?"ace_cjk ace_invisible":"ace_cjk",k=e.showInvisibles?e.SPACE_CHAR:"";return b+=1,""+k+""}if(a.match(/[\v\f \u00a0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000]/)){if(e.showInvisibles){var k=(new Array(a.length+1)).join(e.SPACE_CHAR);return""+k+""}return" "}return b+=1,""+a+""},i=d.replace(f,h);if(!this.$textToken[c.type]){var j="ace_"+c.type.replace(/\./g," ace_"),k="";c.type=="fold"&&(k=" style='width:"+c.value.length*this.config.characterWidth+"px;' "),a.push("",i,"")}else a.push(i);return b+d.length},this.$renderLineCore=function(a,b,c,d,e){var f=0,g=0,h,i=0,j=this;!d||d.length==0?h=Number.MAX_VALUE:h=d[0],e||a.push("
    ");for(var k=0;k=h)i=j.$renderToken(a,i,l,m.substring(0,h-f)),m=m.substring(h-f),f=h,e||a.push("
    ","
    "),g++,i=0,h=d[g]||Number.MAX_VALUE;m.length!=0&&(f+=m.length,i=j.$renderToken(a,i,l,m))}}this.showInvisibles&&(b!==this.session.getLength()-1?a.push(""+this.EOL_CHAR+""):a.push(""+this.EOF_CHAR+"")),e||a.push("
    ")},this.$renderLine=function(a,b,c,d){if(!this.session.isRowFolded(b)){var e=this.session.getRowSplitData(b);this.$renderLineCore(a,b,c,e,d)}else this.$renderFoldLine(a,b,c,d)},this.$renderFoldLine=function(a,b,c,d){function h(a,b,c){var d=0,e=0;while(e+a[d].value.lengthc-b&&(f=f.substring(0,c-b)),g.push({type:a[d].type,value:f}),e=b+f.length,d+=1}while(ec&&(f=f.substring(0,c-e)),g.push({type:a[d].type,value:f}),e+=f.length,d+=1}}var e=this.session,f=e.getFoldLine(b),g=[];f.walk(function(a,b,d,e,f){a?g.push({type:"fold",value:a}):(f&&(c=this.session.getTokens(b,b)[0].tokens),c.length!=0&&h(c,e,d))}.bind(this),f.end.row,this.session.getLine(f.end.row).length);var i=this.session.$useWrapMode?this.session.$wrapData[b]:null;this.$renderLineCore(a,b,g,i,d)},this.$useLineGroups=function(){return this.session.getUseWrapMode()},this.destroy=function(){clearInterval(this.$pollSizeChangesTimer),this.$measureNode&&this.$measureNode.parentNode.removeChild(this.$measureNode),delete this.$measureNode}}).call(i.prototype),b.Text=i}),define("ace/layer/cursor",["require","exports","module","ace/lib/dom"],function(a,b,c){"use strict";var d=a("../lib/dom"),e=function(a){this.element=d.createElement("div"),this.element.className="ace_layer ace_cursor-layer",a.appendChild(this.element),this.isVisible=!1,this.cursors=[],this.cursor=this.addCursor()};(function(){this.$padding=0,this.setPadding=function(a){this.$padding=a},this.setSession=function(a){this.session=a},this.addCursor=function(){var a=d.createElement("div"),b="ace_cursor";return this.isVisible||(b+=" ace_hidden"),this.overwrite&&(b+=" ace_overwrite"),a.className=b,this.element.appendChild(a),this.cursors.push(a),a},this.removeCursor=function(){if(this.cursors.length>1){var a=this.cursors.pop();return a.parentNode.removeChild(a),a}},this.hideCursor=function(){this.isVisible=!1;for(var a=this.cursors.length;a--;)d.addCssClass(this.cursors[a],"ace_hidden");clearInterval(this.blinkId)},this.showCursor=function(){this.isVisible=!0;for(var a=this.cursors.length;a--;)d.removeCssClass(this.cursors[a],"ace_hidden");this.element.style.visibility="",this.restartTimer()},this.restartTimer=function(){clearInterval(this.blinkId);if(!this.isVisible)return;var a=this.element;this.blinkId=setInterval(function(){a.style.visibility="hidden",setTimeout(function(){a.style.visibility="visible"},400)},1e3)},this.getPixelPosition=function(a,b){if(!this.config||!this.session)return{left:0,top:0};a||(a=this.session.selection.getCursor());var c=this.session.documentToScreenPosition(a),d=Math.round(this.$padding+c.column*this.config.characterWidth),e=(c.row-(b?this.config.firstRowScreen:0))*this.config.lineHeight;return{left:d,top:e}},this.update=function(a){this.config=a;if(this.session.selectionMarkerCount>1){var b=this.session.$selectionMarkers,c=0,d,e=0;for(var c=b.length;c--;){d=b[c];var f=this.getPixelPosition(d.cursor,!0),g=(this.cursors[e++]||this.addCursor()).style;g.left=f.left+"px",g.top=f.top+"px",g.width=a.characterWidth+"px",g.height=a.lineHeight+"px"}if(e>1)while(this.cursors.length>e)this.removeCursor()}else{var f=this.getPixelPosition(null,!0),g=this.cursor.style;g.left=f.left+"px",g.top=f.top+"px",g.width=a.characterWidth+"px",g.height=a.lineHeight+"px";while(this.cursors.length>1)this.removeCursor()}var h=this.session.getOverwrite();h!=this.overwrite&&this.$setOverite(h),this.restartTimer()},this.$setOverite=function(a){this.overwrite=a;for(var b=this.cursors.length;b--;)a?d.addCssClass(this.cursors[b],"ace_overwrite"):d.removeCssClass(this.cursors[b],"ace_overwrite")},this.destroy=function(){clearInterval(this.blinkId)}}).call(e.prototype),b.Cursor=e}),define("ace/scrollbar",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/event","ace/lib/event_emitter"],function(a,b,c){"use strict";var d=a("./lib/oop"),e=a("./lib/dom"),f=a("./lib/event"),g=a("./lib/event_emitter").EventEmitter,h=function(a){this.element=e.createElement("div"),this.element.className="ace_sb",this.inner=e.createElement("div"),this.element.appendChild(this.inner),a.appendChild(this.element),this.width=e.scrollbarWidth(a.ownerDocument),this.element.style.width=(this.width||15)+5+"px",f.addListener(this.element,"scroll",this.onScroll.bind(this))};(function(){d.implement(this,g),this.onScroll=function(){this._emit("scroll",{data:this.element.scrollTop})},this.getWidth=function(){return this.width},this.setHeight=function(a){this.element.style.height=a+"px"},this.setInnerHeight=function(a){this.inner.style.height=a+"px"},this.setScrollTop=function(a){this.element.scrollTop=a}}).call(h.prototype),b.ScrollBar=h}),define("ace/renderloop",["require","exports","module","ace/lib/event"],function(a,b,c){"use strict";var d=a("./lib/event"),e=function(a,b){this.onRender=a,this.pending=!1,this.changes=0,this.window=b||window};(function(){this.schedule=function(a){this.changes=this.changes|a;if(!this.pending){this.pending=!0;var b=this;d.nextTick(function(){b.pending=!1;var a;while(a=b.changes)b.changes=0,b.onRender(a)},this.window)}}}).call(e.prototype),b.RenderLoop=e}),define("text!ace/css/editor.css",[],".ace_editor {\n position: absolute;\n overflow: hidden;\n font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Droid Sans Mono', 'Consolas', monospace;\n font-size: 12px;\n}\n\n.ace_scroller {\n position: absolute;\n overflow-x: scroll;\n overflow-y: hidden;\n}\n\n.ace_content {\n position: absolute;\n box-sizing: border-box;\n -moz-box-sizing: border-box;\n -webkit-box-sizing: border-box;\n cursor: text;\n}\n\n.ace_composition {\n position: absolute;\n background: #555;\n color: #DDD;\n z-index: 4;\n}\n\n.ace_gutter {\n position: absolute;\n overflow : hidden;\n height: 100%;\n width: auto;\n cursor: default;\n z-index: 1000;\n}\n\n.ace_gutter.horscroll {\n box-shadow: 0px 0px 20px rgba(0,0,0,0.4);\n}\n\n.ace_gutter-cell {\n padding-left: 19px;\n padding-right: 6px;\n}\n\n.ace_gutter-cell.ace_error {\n background-image: url(\"data:image/gif,GIF89a%10%00%10%00%D5%00%00%F5or%F5%87%88%F5nr%F4ns%EBmq%F5z%7F%DDJT%DEKS%DFOW%F1Yc%F2ah%CE(7%CE)8%D18E%DD%40M%F2KZ%EBU%60%F4%60m%DCir%C8%16(%C8%19*%CE%255%F1%3FR%F1%3FS%E6%AB%B5%CA%5DI%CEn%5E%F7%A2%9A%C9G%3E%E0a%5B%F7%89%85%F5yy%F6%82%80%ED%82%80%FF%BF%BF%E3%C4%C4%FF%FF%FF%FF%FF%FF%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00!%F9%04%01%00%00%25%00%2C%00%00%00%00%10%00%10%00%00%06p%C0%92pH%2C%1A%8F%C8%D2H%93%E1d4%23%E4%88%D3%09mB%1DN%B48%F5%90%40%60%92G%5B%94%20%3E%22%D2%87%24%FA%20%24%C5%06A%00%20%B1%07%02B%A38%89X.v%17%82%11%13q%10%0Fi%24%0F%8B%10%7BD%12%0Ei%09%92%09%0EpD%18%15%24%0A%9Ci%05%0C%18F%18%0B%07%04%01%04%06%A0H%18%12%0D%14%0D%12%A1I%B3%B4%B5IA%00%3B\");\n background-repeat: no-repeat;\n background-position: 2px center;\n}\n\n.ace_gutter-cell.ace_warning {\n background-image: url(\"data:image/gif,GIF89a%10%00%10%00%D5%00%00%FF%DBr%FF%DE%81%FF%E2%8D%FF%E2%8F%FF%E4%96%FF%E3%97%FF%E5%9D%FF%E6%9E%FF%EE%C1%FF%C8Z%FF%CDk%FF%D0s%FF%D4%81%FF%D5%82%FF%D5%83%FF%DC%97%FF%DE%9D%FF%E7%B8%FF%CCl%7BQ%13%80U%15%82W%16%81U%16%89%5B%18%87%5B%18%8C%5E%1A%94d%1D%C5%83-%C9%87%2F%C6%84.%C6%85.%CD%8B2%C9%871%CB%8A3%CD%8B5%DC%98%3F%DF%9BB%E0%9CC%E1%A5U%CB%871%CF%8B5%D1%8D6%DB%97%40%DF%9AB%DD%99B%E3%B0p%E7%CC%AE%FF%FF%FF%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00!%F9%04%01%00%00%2F%00%2C%00%00%00%00%10%00%10%00%00%06a%C0%97pH%2C%1A%8FH%A1%ABTr%25%87%2B%04%82%F4%7C%B9X%91%08%CB%99%1C!%26%13%84*iJ9(%15G%CA%84%14%01%1A%97%0C%03%80%3A%9A%3E%81%84%3E%11%08%B1%8B%20%02%12%0F%18%1A%0F%0A%03'F%1C%04%0B%10%16%18%10%0B%05%1CF%1D-%06%07%9A%9A-%1EG%1B%A0%A1%A0U%A4%A5%A6BA%00%3B\");\n background-repeat: no-repeat;\n background-position: 2px center;\n}\n\n.ace_gutter-cell.ace_info {\n background-image: url(\"data:image/gif;base64,R0lGODlhEAAQAMQAAAAAAEFBQVJSUl5eXmRkZGtra39/f4WFhYmJiZGRkaampry8vMPDw8zMzNXV1dzc3OTk5Orq6vDw8P///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABQALAAAAAAQABAAAAUuICWOZGmeaBml5XGwFCQSBGyXRSAwtqQIiRuiwIM5BoYVbEFIyGCQoeJGrVptIQA7\");\n background-repeat: no-repeat;\n background-position: 2px center;\n}\n\n.ace_editor .ace_sb {\n position: absolute;\n overflow-x: hidden;\n overflow-y: scroll;\n right: 0;\n}\n\n.ace_editor .ace_sb div {\n position: absolute;\n width: 1px;\n left: 0;\n}\n\n.ace_editor .ace_print_margin_layer {\n z-index: 0;\n position: absolute;\n overflow: hidden;\n margin: 0;\n left: 0;\n height: 100%;\n width: 100%;\n}\n\n.ace_editor .ace_print_margin {\n position: absolute;\n height: 100%;\n}\n\n.ace_editor textarea {\n position: fixed;\n z-index: 0;\n width: 10px;\n height: 30px;\n opacity: 0;\n background: transparent;\n appearance: none;\n -moz-appearance: none;\n border: none;\n resize: none;\n outline: none;\n overflow: hidden;\n}\n\n.ace_layer {\n z-index: 1;\n position: absolute;\n overflow: hidden;\n white-space: nowrap;\n height: 100%;\n width: 100%;\n box-sizing: border-box;\n -moz-box-sizing: border-box;\n -webkit-box-sizing: border-box;\n /* setting pointer-events: auto; on node under the mouse, which changes\n during scroll, will break mouse wheel scrolling in Safari */\n pointer-events: none;\n}\n\n.ace_gutter .ace_layer {\n position: relative;\n min-width: 40px;\n text-align: right;\n pointer-events: auto;\n}\n\n.ace_text-layer {\n color: black;\n}\n\n.ace_cjk {\n display: inline-block;\n text-align: center;\n}\n\n.ace_cursor-layer {\n z-index: 4;\n}\n\n.ace_cursor {\n z-index: 4;\n position: absolute;\n}\n\n.ace_cursor.ace_hidden {\n opacity: 0.2;\n}\n\n.ace_line {\n white-space: nowrap;\n}\n\n.ace_marker-layer .ace_step {\n position: absolute;\n z-index: 3;\n}\n\n.ace_marker-layer .ace_selection {\n position: absolute;\n z-index: 5;\n}\n\n.ace_marker-layer .ace_bracket {\n position: absolute;\n z-index: 6;\n}\n\n.ace_marker-layer .ace_active_line {\n position: absolute;\n z-index: 2;\n}\n\n.ace_gutter .ace_gutter_active_line{\n background-color : #dcdcdc;\n}\n\n.ace_marker-layer .ace_selected_word {\n position: absolute;\n z-index: 4;\n box-sizing: border-box;\n -moz-box-sizing: border-box;\n -webkit-box-sizing: border-box;\n}\n\n.ace_line .ace_fold {\n box-sizing: border-box;\n -moz-box-sizing: border-box;\n -webkit-box-sizing: border-box;\n \n display: inline-block;\n height: 11px;\n margin-top: -2px;\n vertical-align: middle;\n\n background-image: \n url(\"data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%11%00%00%00%09%08%06%00%00%00%D4%E8%C7%0C%00%00%03%1EiCCPICC%20Profile%00%00x%01%85T%DFk%D3P%14%FE%DAe%9D%B0%E1%8B%3Ag%11%09%3Eh%91ndStC%9C%B6kW%BA%CDZ%EA6%B7!H%9B%A6m%5C%9A%C6%24%ED~%B0%07%D9%8Bo%3A%C5w%F1%07%3E%F9%07%0C%D9%83o%7B%92%0D%C6%14a%F8%AC%88%22L%F6%22%B3%9E%9B4M'S%03%B9%F7%BB%DF%F9%EE9'%E7%E4%5E%A0%F9qZ%D3%14%2F%0F%14USO%C5%C2%FC%C4%E4%14%DF%F2%01%5E%1CC%2B%FChM%8B%86%16J%26G%40%0F%D3%B2y%EF%B3%F3%0E%1E%C6lt%EEo%DF%AB%FEc%D5%9A%95%0C%11%F0%1C%20%BE%945%C4%22%E1Y%A0i%5C%D4t%13%E0%D6%89%EF%9D15%C2%CDLsX%A7%04%09%1Fg8oc%81%E1%8C%8D%23%96f45%40%9A%09%C2%07%C5B%3AK%B8%408%98i%E0%F3%0D%D8%CE%81%14%E4'%26%A9%92.%8B%3C%ABER%2F%E5dE%B2%0C%F6%F0%1Fs%83%F2_%B0%A8%94%E9%9B%AD%E7%10%8Dm%9A%19N%D1%7C%8A%DE%1F9%7Dp%8C%E6%00%D5%C1%3F_%18%BDA%B8%9DpX6%E3%A35~B%CD%24%AE%11%26%BD%E7%EEti%98%EDe%9A%97Y)%12%25%1C%24%BCbT%AE3li%E6%0B%03%89%9A%E6%D3%ED%F4P%92%B0%9F4%BF43Y%F3%E3%EDP%95%04%EB1%C5%F5%F6KF%F4%BA%BD%D7%DB%91%93%07%E35%3E%A7)%D6%7F%40%FE%BD%F7%F5r%8A%E5y%92%F0%EB%B4%1E%8D%D5%F4%5B%92%3AV%DB%DB%E4%CD%A6%23%C3%C4wQ%3F%03HB%82%8E%1Cd(%E0%91B%0Ca%9Ac%C4%AA%F8L%16%19%22J%A4%D2itTy%B28%D6%3B(%93%96%ED%1CGx%C9_%0E%B8%5E%16%F5%5B%B2%B8%F6%E0%FB%9E%DD%25%D7%8E%BC%15%85%C5%B7%A3%D8Q%ED%B5%81%E9%BA%B2%13%9A%1B%7Fua%A5%A3n%E17%B9%E5%9B%1Bm%AB%0B%08Q%FE%8A%E5%B1H%5Ee%CAO%82Q%D7u6%E6%90S%97%FCu%0B%CF2%94%EE%25v%12X%0C%BA%AC%F0%5E%F8*l%0AO%85%17%C2%97%BF%D4%C8%CE%DE%AD%11%CB%80q%2C%3E%AB%9ES%CD%C6%EC%25%D2L%D2%EBd%B8%BF%8A%F5B%C6%18%F9%901CZ%9D%BE%24M%9C%8A9%F2%DAP%0B'%06w%82%EB%E6%E2%5C%2F%D7%07%9E%BB%CC%5D%E1%FA%B9%08%AD.r%23%8E%C2%17%F5E%7C!%F0%BE3%BE%3E_%B7o%88a%A7%DB%BE%D3d%EB%A31Z%EB%BB%D3%91%BA%A2%B1z%94%8F%DB'%F6%3D%8E%AA%13%19%B2%B1%BE%B1~V%08%2B%B4%A2cjJ%B3tO%00%03%25mN%97%F3%05%93%EF%11%84%0B%7C%88%AE-%89%8F%ABbW%90O%2B%0Ao%99%0C%5E%97%0CI%AFH%D9.%B0%3B%8F%ED%03%B6S%D6%5D%E6i_s9%F3*p%E9%1B%FD%C3%EB.7U%06%5E%19%C0%D1s.%17%A03u%E4%09%B0%7C%5E%2C%EB%15%DB%1F%3C%9E%B7%80%91%3B%DBc%AD%3Dma%BA%8B%3EV%AB%DBt.%5B%1E%01%BB%0F%AB%D5%9F%CF%AA%D5%DD%E7%E4%7F%0Bx%A3%FC%06%A9%23%0A%D6%C2%A1_2%00%00%00%09pHYs%00%00%0B%13%00%00%0B%13%01%00%9A%9C%18%00%00%00%B5IDAT(%15%A5%91%3D%0E%02!%10%85ac%E1%05%D6%CE%D6%C6%CE%D2%E8%ED%CD%DE%C0%C6%D6N.%E0V%F8%3D%9Ca%891XH%C2%BE%D9y%3F%90!%E6%9C%C3%BFk%E5%011%C6-%F5%C8N%04%DF%BD%FF%89%DFt%83DN%60%3E%F3%AB%A0%DE%1A%5Dg%BE%10Q%97%1B%40%9C%A8o%10%8F%5E%828%B4%1B%60%87%F6%02%26%85%1Ch%1E%C1%2B%5Bk%FF%86%EE%B7j%09%9A%DA%9B%ACe%A3%F9%EC%DA!9%B4%D5%A6%81%86%86%98%CC%3C%5B%40%FA%81%B3%E9%CB%23%94%C16Azo%05%D4%E1%C1%95a%3B%8A'%A0%E8%CC%17%22%85%1D%BA%00%A2%FA%DC%0A%94%D1%D1%8D%8B%3A%84%17B%C7%60%1A%25Z%FC%8D%00%00%00%00IEND%AEB%60%82\"),\n url(\"data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%05%00%00%007%08%06%00%00%00%C4%DD%80C%00%00%03%1EiCCPICC%20Profile%00%00x%01%85T%DFk%D3P%14%FE%DAe%9D%B0%E1%8B%3Ag%11%09%3Eh%91ndStC%9C%B6kW%BA%CDZ%EA6%B7!H%9B%A6m%5C%9A%C6%24%ED~%B0%07%D9%8Bo%3A%C5w%F1%07%3E%F9%07%0C%D9%83o%7B%92%0D%C6%14a%F8%AC%88%22L%F6%22%B3%9E%9B4M'S%03%B9%F7%BB%DF%F9%EE9'%E7%E4%5E%A0%F9qZ%D3%14%2F%0F%14USO%C5%C2%FC%C4%E4%14%DF%F2%01%5E%1CC%2B%FChM%8B%86%16J%26G%40%0F%D3%B2y%EF%B3%F3%0E%1E%C6lt%EEo%DF%AB%FEc%D5%9A%95%0C%11%F0%1C%20%BE%945%C4%22%E1Y%A0i%5C%D4t%13%E0%D6%89%EF%9D15%C2%CDLsX%A7%04%09%1Fg8oc%81%E1%8C%8D%23%96f45%40%9A%09%C2%07%C5B%3AK%B8%408%98i%E0%F3%0D%D8%CE%81%14%E4'%26%A9%92.%8B%3C%ABER%2F%E5dE%B2%0C%F6%F0%1Fs%83%F2_%B0%A8%94%E9%9B%AD%E7%10%8Dm%9A%19N%D1%7C%8A%DE%1F9%7Dp%8C%E6%00%D5%C1%3F_%18%BDA%B8%9DpX6%E3%A35~B%CD%24%AE%11%26%BD%E7%EEti%98%EDe%9A%97Y)%12%25%1C%24%BCbT%AE3li%E6%0B%03%89%9A%E6%D3%ED%F4P%92%B0%9F4%BF43Y%F3%E3%EDP%95%04%EB1%C5%F5%F6KF%F4%BA%BD%D7%DB%91%93%07%E35%3E%A7)%D6%7F%40%FE%BD%F7%F5r%8A%E5y%92%F0%EB%B4%1E%8D%D5%F4%5B%92%3AV%DB%DB%E4%CD%A6%23%C3%C4wQ%3F%03HB%82%8E%1Cd(%E0%91B%0Ca%9Ac%C4%AA%F8L%16%19%22J%A4%D2itTy%B28%D6%3B(%93%96%ED%1CGx%C9_%0E%B8%5E%16%F5%5B%B2%B8%F6%E0%FB%9E%DD%25%D7%8E%BC%15%85%C5%B7%A3%D8Q%ED%B5%81%E9%BA%B2%13%9A%1B%7Fua%A5%A3n%E17%B9%E5%9B%1Bm%AB%0B%08Q%FE%8A%E5%B1H%5Ee%CAO%82Q%D7u6%E6%90S%97%FCu%0B%CF2%94%EE%25v%12X%0C%BA%AC%F0%5E%F8*l%0AO%85%17%C2%97%BF%D4%C8%CE%DE%AD%11%CB%80q%2C%3E%AB%9ES%CD%C6%EC%25%D2L%D2%EBd%B8%BF%8A%F5B%C6%18%F9%901CZ%9D%BE%24M%9C%8A9%F2%DAP%0B'%06w%82%EB%E6%E2%5C%2F%D7%07%9E%BB%CC%5D%E1%FA%B9%08%AD.r%23%8E%C2%17%F5E%7C!%F0%BE3%BE%3E_%B7o%88a%A7%DB%BE%D3d%EB%A31Z%EB%BB%D3%91%BA%A2%B1z%94%8F%DB'%F6%3D%8E%AA%13%19%B2%B1%BE%B1~V%08%2B%B4%A2cjJ%B3tO%00%03%25mN%97%F3%05%93%EF%11%84%0B%7C%88%AE-%89%8F%ABbW%90O%2B%0Ao%99%0C%5E%97%0CI%AFH%D9.%B0%3B%8F%ED%03%B6S%D6%5D%E6i_s9%F3*p%E9%1B%FD%C3%EB.7U%06%5E%19%C0%D1s.%17%A03u%E4%09%B0%7C%5E%2C%EB%15%DB%1F%3C%9E%B7%80%91%3B%DBc%AD%3Dma%BA%8B%3EV%AB%DBt.%5B%1E%01%BB%0F%AB%D5%9F%CF%AA%D5%DD%E7%E4%7F%0Bx%A3%FC%06%A9%23%0A%D6%C2%A1_2%00%00%00%09pHYs%00%00%0B%13%00%00%0B%13%01%00%9A%9C%18%00%00%00%3AIDAT8%11c%FC%FF%FF%7F%18%03%1A%60%01%F2%3F%A0%891%80%04%FF%11-%F8%17%9BJ%E2%05%B1ZD%81v%26t%E7%80%F8%A3%82h%A12%1A%20%A3%01%02%0F%01%BA%25%06%00%19%C0%0D%AEF%D5%3ES%00%00%00%00IEND%AEB%60%82\");\n background-repeat: no-repeat, repeat-x;\n background-position: center center, top left;\n color: transparent;\n\n border: 1px solid black;\n -moz-border-radius: 2px;\n -webkit-border-radius: 2px;\n border-radius: 2px;\n \n cursor: pointer;\n pointer-events: auto;\n}\n\n.ace_dark .ace_fold {\n}\n\n.ace_fold:hover{\n background-image: \n url(\"data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%11%00%00%00%09%08%06%00%00%00%D4%E8%C7%0C%00%00%03%1EiCCPICC%20Profile%00%00x%01%85T%DFk%D3P%14%FE%DAe%9D%B0%E1%8B%3Ag%11%09%3Eh%91ndStC%9C%B6kW%BA%CDZ%EA6%B7!H%9B%A6m%5C%9A%C6%24%ED~%B0%07%D9%8Bo%3A%C5w%F1%07%3E%F9%07%0C%D9%83o%7B%92%0D%C6%14a%F8%AC%88%22L%F6%22%B3%9E%9B4M'S%03%B9%F7%BB%DF%F9%EE9'%E7%E4%5E%A0%F9qZ%D3%14%2F%0F%14USO%C5%C2%FC%C4%E4%14%DF%F2%01%5E%1CC%2B%FChM%8B%86%16J%26G%40%0F%D3%B2y%EF%B3%F3%0E%1E%C6lt%EEo%DF%AB%FEc%D5%9A%95%0C%11%F0%1C%20%BE%945%C4%22%E1Y%A0i%5C%D4t%13%E0%D6%89%EF%9D15%C2%CDLsX%A7%04%09%1Fg8oc%81%E1%8C%8D%23%96f45%40%9A%09%C2%07%C5B%3AK%B8%408%98i%E0%F3%0D%D8%CE%81%14%E4'%26%A9%92.%8B%3C%ABER%2F%E5dE%B2%0C%F6%F0%1Fs%83%F2_%B0%A8%94%E9%9B%AD%E7%10%8Dm%9A%19N%D1%7C%8A%DE%1F9%7Dp%8C%E6%00%D5%C1%3F_%18%BDA%B8%9DpX6%E3%A35~B%CD%24%AE%11%26%BD%E7%EEti%98%EDe%9A%97Y)%12%25%1C%24%BCbT%AE3li%E6%0B%03%89%9A%E6%D3%ED%F4P%92%B0%9F4%BF43Y%F3%E3%EDP%95%04%EB1%C5%F5%F6KF%F4%BA%BD%D7%DB%91%93%07%E35%3E%A7)%D6%7F%40%FE%BD%F7%F5r%8A%E5y%92%F0%EB%B4%1E%8D%D5%F4%5B%92%3AV%DB%DB%E4%CD%A6%23%C3%C4wQ%3F%03HB%82%8E%1Cd(%E0%91B%0Ca%9Ac%C4%AA%F8L%16%19%22J%A4%D2itTy%B28%D6%3B(%93%96%ED%1CGx%C9_%0E%B8%5E%16%F5%5B%B2%B8%F6%E0%FB%9E%DD%25%D7%8E%BC%15%85%C5%B7%A3%D8Q%ED%B5%81%E9%BA%B2%13%9A%1B%7Fua%A5%A3n%E17%B9%E5%9B%1Bm%AB%0B%08Q%FE%8A%E5%B1H%5Ee%CAO%82Q%D7u6%E6%90S%97%FCu%0B%CF2%94%EE%25v%12X%0C%BA%AC%F0%5E%F8*l%0AO%85%17%C2%97%BF%D4%C8%CE%DE%AD%11%CB%80q%2C%3E%AB%9ES%CD%C6%EC%25%D2L%D2%EBd%B8%BF%8A%F5B%C6%18%F9%901CZ%9D%BE%24M%9C%8A9%F2%DAP%0B'%06w%82%EB%E6%E2%5C%2F%D7%07%9E%BB%CC%5D%E1%FA%B9%08%AD.r%23%8E%C2%17%F5E%7C!%F0%BE3%BE%3E_%B7o%88a%A7%DB%BE%D3d%EB%A31Z%EB%BB%D3%91%BA%A2%B1z%94%8F%DB'%F6%3D%8E%AA%13%19%B2%B1%BE%B1~V%08%2B%B4%A2cjJ%B3tO%00%03%25mN%97%F3%05%93%EF%11%84%0B%7C%88%AE-%89%8F%ABbW%90O%2B%0Ao%99%0C%5E%97%0CI%AFH%D9.%B0%3B%8F%ED%03%B6S%D6%5D%E6i_s9%F3*p%E9%1B%FD%C3%EB.7U%06%5E%19%C0%D1s.%17%A03u%E4%09%B0%7C%5E%2C%EB%15%DB%1F%3C%9E%B7%80%91%3B%DBc%AD%3Dma%BA%8B%3EV%AB%DBt.%5B%1E%01%BB%0F%AB%D5%9F%CF%AA%D5%DD%E7%E4%7F%0Bx%A3%FC%06%A9%23%0A%D6%C2%A1_2%00%00%00%09pHYs%00%00%0B%13%00%00%0B%13%01%00%9A%9C%18%00%00%00%B5IDAT(%15%A5%91%3D%0E%02!%10%85ac%E1%05%D6%CE%D6%C6%CE%D2%E8%ED%CD%DE%C0%C6%D6N.%E0V%F8%3D%9Ca%891XH%C2%BE%D9y%3F%90!%E6%9C%C3%BFk%E5%011%C6-%F5%C8N%04%DF%BD%FF%89%DFt%83DN%60%3E%F3%AB%A0%DE%1A%5Dg%BE%10Q%97%1B%40%9C%A8o%10%8F%5E%828%B4%1B%60%87%F6%02%26%85%1Ch%1E%C1%2B%5Bk%FF%86%EE%B7j%09%9A%DA%9B%ACe%A3%F9%EC%DA!9%B4%D5%A6%81%86%86%98%CC%3C%5B%40%FA%81%B3%E9%CB%23%94%C16Azo%05%D4%E1%C1%95a%3B%8A'%A0%E8%CC%17%22%85%1D%BA%00%A2%FA%DC%0A%94%D1%D1%8D%8B%3A%84%17B%C7%60%1A%25Z%FC%8D%00%00%00%00IEND%AEB%60%82\"),\n url(\"data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%05%00%00%007%08%06%00%00%00%C4%DD%80C%00%00%03%1EiCCPICC%20Profile%00%00x%01%85T%DFk%D3P%14%FE%DAe%9D%B0%E1%8B%3Ag%11%09%3Eh%91ndStC%9C%B6kW%BA%CDZ%EA6%B7!H%9B%A6m%5C%9A%C6%24%ED~%B0%07%D9%8Bo%3A%C5w%F1%07%3E%F9%07%0C%D9%83o%7B%92%0D%C6%14a%F8%AC%88%22L%F6%22%B3%9E%9B4M'S%03%B9%F7%BB%DF%F9%EE9'%E7%E4%5E%A0%F9qZ%D3%14%2F%0F%14USO%C5%C2%FC%C4%E4%14%DF%F2%01%5E%1CC%2B%FChM%8B%86%16J%26G%40%0F%D3%B2y%EF%B3%F3%0E%1E%C6lt%EEo%DF%AB%FEc%D5%9A%95%0C%11%F0%1C%20%BE%945%C4%22%E1Y%A0i%5C%D4t%13%E0%D6%89%EF%9D15%C2%CDLsX%A7%04%09%1Fg8oc%81%E1%8C%8D%23%96f45%40%9A%09%C2%07%C5B%3AK%B8%408%98i%E0%F3%0D%D8%CE%81%14%E4'%26%A9%92.%8B%3C%ABER%2F%E5dE%B2%0C%F6%F0%1Fs%83%F2_%B0%A8%94%E9%9B%AD%E7%10%8Dm%9A%19N%D1%7C%8A%DE%1F9%7Dp%8C%E6%00%D5%C1%3F_%18%BDA%B8%9DpX6%E3%A35~B%CD%24%AE%11%26%BD%E7%EEti%98%EDe%9A%97Y)%12%25%1C%24%BCbT%AE3li%E6%0B%03%89%9A%E6%D3%ED%F4P%92%B0%9F4%BF43Y%F3%E3%EDP%95%04%EB1%C5%F5%F6KF%F4%BA%BD%D7%DB%91%93%07%E35%3E%A7)%D6%7F%40%FE%BD%F7%F5r%8A%E5y%92%F0%EB%B4%1E%8D%D5%F4%5B%92%3AV%DB%DB%E4%CD%A6%23%C3%C4wQ%3F%03HB%82%8E%1Cd(%E0%91B%0Ca%9Ac%C4%AA%F8L%16%19%22J%A4%D2itTy%B28%D6%3B(%93%96%ED%1CGx%C9_%0E%B8%5E%16%F5%5B%B2%B8%F6%E0%FB%9E%DD%25%D7%8E%BC%15%85%C5%B7%A3%D8Q%ED%B5%81%E9%BA%B2%13%9A%1B%7Fua%A5%A3n%E17%B9%E5%9B%1Bm%AB%0B%08Q%FE%8A%E5%B1H%5Ee%CAO%82Q%D7u6%E6%90S%97%FCu%0B%CF2%94%EE%25v%12X%0C%BA%AC%F0%5E%F8*l%0AO%85%17%C2%97%BF%D4%C8%CE%DE%AD%11%CB%80q%2C%3E%AB%9ES%CD%C6%EC%25%D2L%D2%EBd%B8%BF%8A%F5B%C6%18%F9%901CZ%9D%BE%24M%9C%8A9%F2%DAP%0B'%06w%82%EB%E6%E2%5C%2F%D7%07%9E%BB%CC%5D%E1%FA%B9%08%AD.r%23%8E%C2%17%F5E%7C!%F0%BE3%BE%3E_%B7o%88a%A7%DB%BE%D3d%EB%A31Z%EB%BB%D3%91%BA%A2%B1z%94%8F%DB'%F6%3D%8E%AA%13%19%B2%B1%BE%B1~V%08%2B%B4%A2cjJ%B3tO%00%03%25mN%97%F3%05%93%EF%11%84%0B%7C%88%AE-%89%8F%ABbW%90O%2B%0Ao%99%0C%5E%97%0CI%AFH%D9.%B0%3B%8F%ED%03%B6S%D6%5D%E6i_s9%F3*p%E9%1B%FD%C3%EB.7U%06%5E%19%C0%D1s.%17%A03u%E4%09%B0%7C%5E%2C%EB%15%DB%1F%3C%9E%B7%80%91%3B%DBc%AD%3Dma%BA%8B%3EV%AB%DBt.%5B%1E%01%BB%0F%AB%D5%9F%CF%AA%D5%DD%E7%E4%7F%0Bx%A3%FC%06%A9%23%0A%D6%C2%A1_2%00%00%00%09pHYs%00%00%0B%13%00%00%0B%13%01%00%9A%9C%18%00%00%003IDAT8%11c%FC%FF%FF%7F%3E%03%1A%60%01%F2%3F%A3%891%80%04%FFQ%26%F8w%C0%B43%A1%DB%0C%E2%8F%0A%A2%85%CAh%80%8C%06%08%3C%04%E8%96%18%00%A3S%0D%CD%CF%D8%C1%9D%00%00%00%00IEND%AEB%60%82\");\n background-repeat: no-repeat, repeat-x;\n background-position: center center, top left;\n}\n\n.ace_dragging .ace_content {\n cursor: move;\n}\n\n.ace_folding-enabled > .ace_gutter-cell {\n padding-right: 13px;\n}\n\n.ace_fold-widget {\n box-sizing: border-box;\n -moz-box-sizing: border-box;\n -webkit-box-sizing: border-box;\n\n margin: 0 -12px 1px 1px;\n display: inline-block;\n height: 14px;\n width: 11px;\n vertical-align: text-bottom;\n \n background-image: url(\"data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%05%00%00%00%05%08%06%00%00%00%8Do%26%E5%00%00%004IDATx%DAe%8A%B1%0D%000%0C%C2%F2%2CK%96%BC%D0%8F9%81%88H%E9%D0%0E%96%C0%10%92%3E%02%80%5E%82%E4%A9*-%EEsw%C8%CC%11%EE%96w%D8%DC%E9*Eh%0C%151(%00%00%00%00IEND%AEB%60%82\");\n background-repeat: no-repeat;\n background-position: center 5px;\n\n border-radius: 3px;\n}\n\n.ace_fold-widget.end {\n background-image: url(\"data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%05%00%00%00%05%08%06%00%00%00%8Do%26%E5%00%00%004IDATx%DAm%C7%C1%09%000%08C%D1%8C%ECE%C8E(%8E%EC%02)%1EZJ%F1%C1'%04%07I%E1%E5%EE%CAL%F5%A2%99%99%22%E2%D6%1FU%B5%FE0%D9x%A7%26Wz5%0E%D5%00%00%00%00IEND%AEB%60%82\");\n}\n\n.ace_fold-widget.closed {\n background-image: url(\"data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%03%00%00%00%06%08%06%00%00%00%06%E5%24%0C%00%00%009IDATx%DA5%CA%C1%09%000%08%03%C0%AC*(%3E%04%C1%0D%BA%B1%23%A4Uh%E0%20%81%C0%CC%F8%82%81%AA%A2%AArGfr%88%08%11%11%1C%DD%7D%E0%EE%5B%F6%F6%CB%B8%05Q%2F%E9tai%D9%00%00%00%00IEND%AEB%60%82\");\n}\n\n.ace_fold-widget:hover {\n border: 1px solid rgba(0, 0, 0, 0.3);\n background-color: rgba(255, 255, 255, 0.2);\n -moz-box-shadow:inset 0 1px 1px rgba(255, 255, 255, 0.7);\n -moz-box-shadow: 0 1px 1px rgba(255, 255, 255, 0.7);\n -webkit-box-shadow:inset 0 1px 1px rgba(255, 255, 255, 0.7);\n -webkit-box-shadow: 0 1px 1px rgba(255, 255, 255, 0.7);\n box-shadow:inset 0 1px 1px rgba(255, 255, 255, 0.7);\n box-shadow: 0 1px 1px rgba(255, 255, 255, 0.7);\n background-position: center 4px;\n}\n\n.ace_fold-widget:active {\n border: 1px solid rgba(0, 0, 0, 0.4);\n background-color: rgba(0, 0, 0, 0.05);\n -moz-box-shadow:inset 0 1px 1px rgba(255, 255, 255);\n -moz-box-shadow: 0 1px 1px rgba(255, 255, 255, 0.8);\n -webkit-box-shadow:inset 0 1px 1px rgba(255, 255, 255);\n -webkit-box-shadow: 0 1px 1px rgba(255, 255, 255, 0.8);\n box-shadow:inset 0 1px 1px rgba(255, 255, 255);\n box-shadow: 0 1px 1px rgba(255, 255, 255, 0.8);\n}\n\n.ace_fold-widget.invalid {\n background-color: #FFB4B4;\n border-color: #DE5555;\n}\n"),define("ace/multi_select",["require","exports","module","ace/range_list","ace/range","ace/selection","ace/mouse/multi_select_handler","ace/commands/multi_select_commands","ace/search","ace/edit_session","ace/editor"],function(a,b,c){function j(a,b,c){return i.$options.wrap=!0,i.$options.needle=b,i.$options.backwards=c==-1,i.find(a)}function m(a,b){return a.row==b.row&&a.column==b.column}function n(a){a.$onAddRange=a.$onAddRange.bind(a),a.$onRemoveRange=a.$onRemoveRange.bind(a),a.$onMultiSelect=a.$onMultiSelect.bind(a),a.$onSingleSelect=a.$onSingleSelect.bind(a),b.onSessionChange.call(a,a),a.on("changeSession",b.onSessionChange.bind(a)),a.on("mousedown",g),a.commands.addCommands(b.commands.defaultCommands)}var d=a("./range_list").RangeList,e=a("./range").Range,f=a("./selection").Selection,g=a("./mouse/multi_select_handler").onMouseDown;b.commands=a("./commands/multi_select_commands");var h=a("./search").Search,i=new h,k=a("./edit_session").EditSession;(function(){this.getSelectionMarkers=function(){return this.$selectionMarkers}}).call(k.prototype),function(){this.ranges=null,this.rangeList=null,this.addRange=function(a){if(!this.inMultiSelectMode&&this.rangeCount==0){var b=this.toOrientedRange();if(!a||!a.isEqual(b))this.rangeList.add(b),this.$onAddRange(b)}if(!a)return;a.cursor||(a.cursor=a.end);var c=this.rangeList.add(a);this.$onAddRange(a),c.length&&this.$onRemoveRange(c),this.rangeCount>0&&!this.inMultiSelectMode&&(this._emit("multiSelect"),this.inMultiSelectMode=!0,this.session.$undoSelect=!1,this.rangeList.attach(this.session))},this.toSingleRange=function(a){a=a||this.ranges[0];var b=this.rangeList.removeAll();b.length&&this.$onRemoveRange(b),a&&this.fromOrientedRange(a)},this.substractPoint=function(a){var b=this.rangeList.substractPoint(a);if(b)return this.$onRemoveRange(b),b[0]},this.mergeOverlappingRanges=function(){var a=this.rangeList.merge();a.length?this.$onRemoveRange(a):this.ranges[0]&&this.fromOrientedRange(this.ranges[0])},this.$onAddRange=function(a){this.rangeCount=this.rangeList.ranges.length,this.ranges.unshift(a),this.fromOrientedRange(a),this._emit("addRange",{range:a})},this.$onRemoveRange=function(a){this.rangeCount=this.rangeList.ranges.length;if(this.rangeCount==1&&this.inMultiSelectMode){var b=this.rangeList.ranges.pop();a.push(b),this.rangeCount=0}for(var c=a.length;c--;){var d=this.ranges.indexOf(a[c]);this.ranges.splice(d,1)}this._emit("removeRange",{ranges:a}),this.rangeCount==0&&this.inMultiSelectMode&&(this.inMultiSelectMode=!1,this._emit("singleSelect"),this.session.$undoSelect=!0,this.rangeList.detach(this.session)),b=b||this.ranges[0],b&&!b.isEqual(this.getRange())&&this.fromOrientedRange(b)},this.$initRangeList=function(){if(this.rangeList)return;this.rangeList=new d,this.ranges=[],this.rangeCount=0},this.getAllRanges=function(){return this.rangeList.ranges.concat()},this.splitIntoLines=function(){if(this.rangeCount>1){var a=this.rangeList.ranges,b=a[a.length-1],c=e.fromPoints(a[0].start,b.end);this.toSingleRange(),this.setSelectionRange(c,b.cursor==b.start)}else{var d=this.session.documentToScreenPosition(this.selectionLead),f=this.session.documentToScreenPosition(this.selectionAnchor),g=this.rectangularRangeBlock(d,f);g.forEach(this.addRange,this)}},this.rectangularRangeBlock=function(a,b,c){var d=[],f=a.column0)p--;if(p>0){var q=0;while(d[q].isEmpty())q++}for(var r=p;r>=q;r--)d[r].isEmpty()&&d.splice(r,1)}return d}}.call(f.prototype);var l=a("./editor").Editor;(function(){this.updateSelectionMarkers=function(){this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.addSelectionMarker=function(a){a.cursor||(a.cursor=a.end);var b=this.getSelectionStyle();return a.marker=this.session.addMarker(a,"ace_selection",b),this.session.$selectionMarkers.push(a),this.session.selectionMarkerCount=this.session.$selectionMarkers.length,a},this.removeSelectionMarkers=function(a){for(var b=a.length;b--;){var c=a[b];if(!c.marker)continue;this.session.removeMarker(c.marker);var d=this.session.$selectionMarkers.indexOf(c);d!=-1&&this.session.$selectionMarkers.splice(d,1)}this.session.selectionMarkerCount=this.session.$selectionMarkers.length},this.$onAddRange=function(a){this.addSelectionMarker(a.range),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onRemoveRange=function(a){this.removeSelectionMarkers(a.ranges),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onMultiSelect=function(a){if(this.inMultiSelectMode)return;this.inMultiSelectMode=!0,this.setStyle("multiselect"),this.keyBinding.addKeyboardHandler(b.commands.keyboardHandler),this.commands.on("exec",this.$onMultiSelectExec),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onSingleSelect=function(a){if(this.session.multiSelect.inVirtualMode)return;this.inMultiSelectMode=!1,this.unsetStyle("multiselect"),this.keyBinding.removeKeyboardHandler(b.commands.keyboardHandler),this.commands.removeEventListener("exec",this.$onMultiSelectExec),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onMultiSelectExec=function(a){var b=a.command,c=a.editor;b.multiSelectAction?b.multiSelectAction=="forEach"?c.forEachSelection(b,a.args):b.multiSelectAction=="single"?(c.exitMultiSelectMode(),b.exec(c,a.args||{})):b.multiSelectAction(c,a.args||{}):(b.exec(c,a.args||{}),c.multiSelect.addRange(c.multiSelect.toOrientedRange()),c.multiSelect.mergeOverlappingRanges()),a.preventDefault()},this.forEachSelection=function(a,b){if(this.inVirtualSelectionMode)return;var c=this.session,d=this.selection,e=d.rangeList,g=d._eventRegistry;d._eventRegistry={};var h=new f(c);this.inVirtualSelectionMode=!0;for(var i=e.ranges.length;i--;)h.fromOrientedRange(e.ranges[i]),this.selection=c.selection=h,a.exec(this,b||{}),h.toOrientedRange(e.ranges[i]);h.detach(),this.selection=c.selection=d,this.inVirtualSelectionMode=!1,d._eventRegistry=g,d.mergeOverlappingRanges(),this.onCursorChange(),this.onSelectionChange()},this.exitMultiSelectMode=function(){if(this.inVirtualSelectionMode)return;this.multiSelect.toSingleRange()},this.getCopyText=function(){var a="";if(this.inMultiSelectMode){var b=this.multiSelect.rangeList.ranges;a=[];for(var c=0;c0)continue;return f==0?d:(f=this.comparePoints(a,e.start),f>=0?d:-d-1)}return-d-1},this.add=function(a){var b=this.pointIndex(a.start);b<0&&(b=-b-1);var c=this.pointIndex(a.end,b);return c<0?c=-c-1:c++,this.ranges.splice(b,c-b,a)},this.addList=function(a){var b=[];for(var c=a.length;c--;)b.push.call(b,this.add(a[c]));return b},this.substractPoint=function(a){var b=this.pointIndex(a);if(b>=0)return this.ranges.splice(b,1)},this.merge=function(){var a=[],b=this.ranges,c=b[0],d;for(var e=1;e=0},this.containsPoint=function(a){return this.pointIndex(a)>=0},this.rangeAtPoint=function(a){var b=this.pointIndex(a);if(b>=0)return this.ranges[b]},this.clipRows=function(a,b){var c=this.ranges;if(c[0].start.row>b||c[c.length-1].start.rowe)break;l.start.row==e&&l.start.column>=c.column&&(l.start.column+=h,l.start.row+=g),l.end.row==e&&l.end.column>=c.column&&(l.end.column+=h,l.end.row+=g)}if(g!=0&&j=this.pos.column&&c.start.column<=this.pos.column+this.length+1){var f=c.start.column-this.pos.column;this.length+=e;if(!this.session.$fromUndo){if(b.action==="insertText")for(var g=this.others.length-1;g>=0;g--){var h=this.others[g],i={row:h.row,column:h.column+f};h.row===c.start.row&&c.start.column=0;g--){var h=this.others[g],i={row:h.row,column:h.column+f};h.row===c.start.row&&c.start.column=this.pos.column&&b.column<=this.pos.column+this.length?(this.showOtherMarkers(),this._emit("cursorEnter",a)):(this.hideOtherMarkers(),this._emit("cursorLeave",a))},this.detach=function(){this.session.removeMarker(this.markerId),this.hideOtherMarkers(),this.doc.removeEventListener("change",this.$onUpdate),this.session.selection.removeEventListener("changeCursor",this.$onCursorChange),this.pos.detach();for(var a=0;aassign("shared_items", OC_Share::getMySharedItems()); -$tmpl->printPage(); - -?> +if( $versions->expireAll() ){ + + OCP\JSON::success(); + die(); + +} else { + + OCP\JSON::error(); + die(); + +} \ No newline at end of file diff --git a/apps/files_versions/ajax/getVersions.php b/apps/files_versions/ajax/getVersions.php index 92ca3ed898..1a0e21732c 100644 --- a/apps/files_versions/ajax/getVersions.php +++ b/apps/files_versions/ajax/getVersions.php @@ -1,15 +1,18 @@ diff --git a/apps/files_versions/appinfo/api.php b/apps/files_versions/appinfo/api.php new file mode 100644 index 0000000000..a7386bc2c9 --- /dev/null +++ b/apps/files_versions/appinfo/api.php @@ -0,0 +1,36 @@ +. +*/ + +return array( + 'list' => array('method' => 'GET', 'class' => 'Storage', 'function' => 'getVersions', + 'parameters' => array( + 'file' => array('required' => true, 'type' => 'string') + ) + ), + 'revert' => array('method' => 'POST', 'class' => 'Storage', 'function' => 'rollback', + 'parameters' => array( + 'file' => array('required' => true, 'type' => 'string'), + 'time' => array('required' => true, 'type' => 'int') + ) + ) +); + +?> \ No newline at end of file diff --git a/apps/files_versions/appinfo/app.php b/apps/files_versions/appinfo/app.php index fd31a0bb67..9ac7f6d5cd 100644 --- a/apps/files_versions/appinfo/app.php +++ b/apps/files_versions/appinfo/app.php @@ -1,17 +1,16 @@ 10, - 'id' => 'files_versions', - 'name' => 'Versioning' )); +//require_once('files_versions/versions.php'); +OC::$CLASSPATH['OCA_Versions\Storage'] = 'apps/files_versions/lib/versions.php'; +OC::$CLASSPATH['OCA_Versions\Hooks'] = 'apps/files_versions/lib/hooks.php'; OCP\App::registerAdmin('files_versions', 'settings'); +OCP\App::registerPersonal('files_versions','settings-personal'); + OCP\Util::addscript('files_versions', 'versions'); // Listen to write signals -OCP\Util::connectHook(OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_write, "OCA_Versions\Storage", "write_hook"); - -?> +OCP\Util::connectHook('OC_Filesystem', 'post_write', "OCA_Versions\Hooks", "write_hook"); +// Listen to delete and rename signals +OCP\Util::connectHook('OC_Filesystem', 'delete', "OCA_Versions\Hooks", "remove_hook"); +OCP\Util::connectHook('OC_Filesystem', 'rename', "OCA_Versions\Hooks", "rename_hook"); \ No newline at end of file diff --git a/apps/files_versions/appinfo/update.php b/apps/files_versions/appinfo/update.php new file mode 100644 index 0000000000..9569ca1048 --- /dev/null +++ b/apps/files_versions/appinfo/update.php @@ -0,0 +1,16 @@ +assign( 'path', $path ); + $versions = new OCA_Versions\Storage(); // roll back to old version if button clicked if( isset( $_GET['revert'] ) ) { - if( \OCA_Versions\Storage::rollback( $path, $_GET['revert'] ) ) { + if( $versions->rollback( $path, $_GET['revert'] ) ) { $tmpl->assign( 'outcome_stat', 'success' ); - $tmpl->assign( 'outcome_msg', "File {$_GET['path']} was reverted to version ".OCP\Util::formatDate( $_GET['revert'] ) ); + $tmpl->assign( 'outcome_msg', "File {$_GET['path']} was reverted to version ".OCP\Util::formatDate( doubleval($_GET['revert']) ) ); } else { $tmpl->assign( 'outcome_stat', 'failure' ); - $tmpl->assign( 'outcome_msg', "File {$_GET['path']} could not be reverted to version ".OCP\Util::formatDate( $_GET['revert'] ) ); + $tmpl->assign( 'outcome_msg', "File {$_GET['path']} could not be reverted to version ".OCP\Util::formatDate( doubleval($_GET['revert']) ) ); } @@ -54,7 +55,7 @@ if ( isset( $_GET['path'] ) ) { if( OCA_Versions\Storage::isversioned( $path ) ) { $count = 999; //show the newest revisions - $versions = OCA_Versions\Storage::getversions( $path, $count ); + $versions = OCA_Versions\Storage::getVersions( $path, $count); $tmpl->assign( 'versions', array_reverse( $versions ) ); @@ -70,5 +71,3 @@ if ( isset( $_GET['path'] ) ) { } $tmpl->printPage( ); - -?> diff --git a/apps/files_versions/js/settings-personal.js b/apps/files_versions/js/settings-personal.js new file mode 100644 index 0000000000..6ea8c1a950 --- /dev/null +++ b/apps/files_versions/js/settings-personal.js @@ -0,0 +1,39 @@ +// TODO: allow the button to be clicked only once + +$( document ).ready(function(){ + // + $( '#expireAllBtn' ).click( + + function( event ) { + + // Prevent page from reloading + event.preventDefault(); + + // Show loading gif + $('.expireAllLoading').show(); + + $.getJSON( + OC.filePath('files_versions','ajax','expireAll.php'), + function(result){ + if (result.status == 'success') { + $('.expireAllLoading').hide(); + $('#expireAllBtn').html('Expiration successful'); + } else { + + // Cancel loading + $('#expireAllBtn').html('Expiration failed'); + + // Show Dialog + OC.dialogs.alert( + 'Something went wrong, your files may not have been expired', + 'An error has occurred', + function(){ + $('#expireAllBtn').html(t('files_versions', 'Expire all versions')+''); + } + ); + } + } + ); + } + ); +}); \ No newline at end of file diff --git a/apps/files_versions/js/versions.js b/apps/files_versions/js/versions.js index 5e46b2a0ee..c5c1553f1a 100644 --- a/apps/files_versions/js/versions.js +++ b/apps/files_versions/js/versions.js @@ -11,7 +11,7 @@ $(document).ready(function() { $(document).ready(function(){ if (typeof FileActions !== 'undefined') { // Add history button to files/index.php - FileActions.register('file','History',function(){return OC.imagePath('core','actions/history')},function(filename){ + FileActions.register('file','History', FileActions.PERMISSION_UPDATE, function(){return OC.imagePath('core','actions/history')},function(filename){ if (scanFiles.scanning){return;}//workaround to prevent additional http request block scanning feedback @@ -33,7 +33,7 @@ $(document).ready(function(){ }); function createVersionsDropdown(filename, files) { - + var historyUrl = OC.linkTo('files_versions', 'history.php') + '?path='+encodeURIComponent( $( '#dir' ).val() ).replace( /%2F/g, '/' )+'/'+encodeURIComponent( filename ); var html = '"); + $('#supersized-loader').remove(); + $('#supersized').remove(); + $('#supersized-holder').append("
      "); + $('#supersized').show(); + $('#slideshow-content').show(); + + + jQuery(function($){ + + $.supersized({ + + // Functionality + slide_interval : 3000, // Length between transitions + transition : 1, // 0-None, 1-Fade, 2-Slide Top, 3-Slide Right, 4-Slide Bottom, 5-Slide Left, 6-Carousel Right, 7-Carousel Left + transition_speed : 700, // Speed of transition + + // Components + slide_links : 'blank', // Individual links for each slide (Options: false, 'num', 'name', 'blank') + slides : images // Slideshow Images + + }); + }); + + }); + + //close slideshow on esc and remove holder + $(document).keyup(function(e) { + if (e.keyCode == 27) { // esc + $.endSlideshow(); + } + }); + +}); diff --git a/apps/gallery/js/supersized.3.2.7.js b/apps/gallery/js/supersized.3.2.7.js new file mode 100644 index 0000000000..f5a1c0bbc2 --- /dev/null +++ b/apps/gallery/js/supersized.3.2.7.js @@ -0,0 +1,930 @@ +/* + + Supersized - Fullscreen Slideshow jQuery Plugin + Version : 3.2.7 + Site : www.buildinternet.com/project/supersized + + Author : Sam Dunn + Company : One Mighty Roar (www.onemightyroar.com) + License : MIT License / GPL License + +*/ + +(function($){ + + /* Place Supersized Elements + ----------------------------*/ + $(document).ready(function() { + $('body').append('
        '); + }); + + + $.supersized = function(options){ + + /* Variables + ----------------------------*/ + var el = '#supersized', + base = this; + // Access to jQuery and DOM versions of element + base.$el = $(el); + base.el = el; + vars = $.supersized.vars; + // Add a reverse reference to the DOM object + base.$el.data("supersized", base); + api = base.$el.data('supersized'); + + base.init = function(){ + // Combine options and vars + $.supersized.vars = $.extend($.supersized.vars, $.supersized.themeVars); + $.supersized.vars.options = $.extend({},$.supersized.defaultOptions, $.supersized.themeOptions, options); + base.options = $.supersized.vars.options; + + base._build(); + }; + + + /* Build Elements + ----------------------------*/ + base._build = function(){ + // Add in slide markers + var thisSlide = 0, + slideSet = '', + markers = '', + markerContent, + thumbMarkers = '', + thumbImage; + + while(thisSlide <= base.options.slides.length-1){ + //Determine slide link content + switch(base.options.slide_links){ + case 'num': + markerContent = thisSlide; + break; + case 'name': + markerContent = base.options.slides[thisSlide].title; + break; + case 'blank': + markerContent = ''; + break; + } + + slideSet = slideSet+'
      • '; + + if(thisSlide == base.options.start_slide-1){ + // Slide links + if (base.options.slide_links)markers = markers+''; + // Slide Thumbnail Links + if (base.options.thumb_links){ + base.options.slides[thisSlide].thumb ? thumbImage = base.options.slides[thisSlide].thumb : thumbImage = base.options.slides[thisSlide].image; + thumbMarkers = thumbMarkers+'
      • '; + }; + }else{ + // Slide links + if (base.options.slide_links) markers = markers+''; + // Slide Thumbnail Links + if (base.options.thumb_links){ + base.options.slides[thisSlide].thumb ? thumbImage = base.options.slides[thisSlide].thumb : thumbImage = base.options.slides[thisSlide].image; + thumbMarkers = thumbMarkers+'
      • '; + }; + } + thisSlide++; + } + + if (base.options.slide_links) $(vars.slide_list).html(markers); + if (base.options.thumb_links && vars.thumb_tray.length){ + $(vars.thumb_tray).append('
          '+thumbMarkers+'
        '); + } + + $(base.el).append(slideSet); + + // Add in thumbnails + if (base.options.thumbnail_navigation){ + // Load previous thumbnail + vars.current_slide - 1 < 0 ? prevThumb = base.options.slides.length - 1 : prevThumb = vars.current_slide - 1; + $(vars.prev_thumb).show().html($("").attr("src", base.options.slides[prevThumb].image)); + + // Load next thumbnail + vars.current_slide == base.options.slides.length - 1 ? nextThumb = 0 : nextThumb = vars.current_slide + 1; + $(vars.next_thumb).show().html($("").attr("src", base.options.slides[nextThumb].image)); + } + + base._start(); // Get things started + }; + + + /* Initialize + ----------------------------*/ + base._start = function(){ + + // Determine if starting slide random + if (base.options.start_slide){ + vars.current_slide = base.options.start_slide - 1; + }else{ + vars.current_slide = Math.floor(Math.random()*base.options.slides.length); // Generate random slide number + } + + // If links should open in new window + var linkTarget = base.options.new_window ? ' target="_blank"' : ''; + + // Set slideshow quality (Supported only in FF and IE, no Webkit) + if (base.options.performance == 3){ + base.$el.addClass('speed'); // Faster transitions + } else if ((base.options.performance == 1) || (base.options.performance == 2)){ + base.$el.addClass('quality'); // Higher image quality + } + + // Shuffle slide order if needed + if (base.options.random){ + arr = base.options.slides; + for(var j, x, i = arr.length; i; j = parseInt(Math.random() * i), x = arr[--i], arr[i] = arr[j], arr[j] = x); // Fisher-Yates shuffle algorithm (jsfromhell.com/array/shuffle) + base.options.slides = arr; + } + + /*-----Load initial set of images-----*/ + + if (base.options.slides.length > 1){ + if(base.options.slides.length > 2){ + // Set previous image + vars.current_slide - 1 < 0 ? loadPrev = base.options.slides.length - 1 : loadPrev = vars.current_slide - 1; // If slide is 1, load last slide as previous + var imageLink = (base.options.slides[loadPrev].url) ? "href='" + base.options.slides[loadPrev].url + "'" : ""; + + var imgPrev = $(''); + var slidePrev = base.el+' li:eq('+loadPrev+')'; + imgPrev.appendTo(slidePrev).wrap('').parent().parent().addClass('image-loading prevslide'); + + imgPrev.load(function(){ + $(this).data('origWidth', $(this).width()).data('origHeight', $(this).height()); + base.resizeNow(); // Resize background image + }); // End Load + } + } else { + // Slideshow turned off if there is only one slide + base.options.slideshow = 0; + } + + // Set current image + imageLink = (api.getField('url')) ? "href='" + api.getField('url') + "'" : ""; + var img = $(''); + + var slideCurrent= base.el+' li:eq('+vars.current_slide+')'; + img.appendTo(slideCurrent).wrap('').parent().parent().addClass('image-loading activeslide'); + + img.load(function(){ + base._origDim($(this)); + base.resizeNow(); // Resize background image + base.launch(); + if( typeof theme != 'undefined' && typeof theme._init == "function" ) theme._init(); // Load Theme + }); + + if (base.options.slides.length > 1){ + // Set next image + vars.current_slide == base.options.slides.length - 1 ? loadNext = 0 : loadNext = vars.current_slide + 1; // If slide is last, load first slide as next + imageLink = (base.options.slides[loadNext].url) ? "href='" + base.options.slides[loadNext].url + "'" : ""; + + var imgNext = $(''); + var slideNext = base.el+' li:eq('+loadNext+')'; + imgNext.appendTo(slideNext).wrap('').parent().parent().addClass('image-loading'); + + imgNext.load(function(){ + $(this).data('origWidth', $(this).width()).data('origHeight', $(this).height()); + base.resizeNow(); // Resize background image + }); // End Load + } + /*-----End load initial images-----*/ + + // Hide elements to be faded in + base.$el.css('visibility','hidden'); + $('.load-item').hide(); + + }; + + + /* Launch Supersized + ----------------------------*/ + base.launch = function(){ + + base.$el.css('visibility','visible'); + $('#supersized-loader').remove(); //Hide loading animation + + // Call theme function for before slide transition + if( typeof theme != 'undefined' && typeof theme.beforeAnimation == "function" ) theme.beforeAnimation('next'); + $('.load-item').show(); + + // Keyboard Navigation + if (base.options.keyboard_nav){ + $(document.documentElement).keyup(function (event) { + + if(vars.in_animation) return false; // Abort if currently animating + + // Left Arrow or Down Arrow + if ((event.keyCode == 37) || (event.keyCode == 40)) { + clearInterval(vars.slideshow_interval); // Stop slideshow, prevent buildup + base.prevSlide(); + + // Right Arrow or Up Arrow + } else if ((event.keyCode == 39) || (event.keyCode == 38)) { + clearInterval(vars.slideshow_interval); // Stop slideshow, prevent buildup + base.nextSlide(); + + // Spacebar + } else if (event.keyCode == 32 && !vars.hover_pause) { + clearInterval(vars.slideshow_interval); // Stop slideshow, prevent buildup + base.playToggle(); + } + + }); + } + + // Pause when hover on image + if (base.options.slideshow && base.options.pause_hover){ + $(base.el).hover(function() { + if(vars.in_animation) return false; // Abort if currently animating + vars.hover_pause = true; // Mark slideshow paused from hover + if(!vars.is_paused){ + vars.hover_pause = 'resume'; // It needs to resume afterwards + base.playToggle(); + } + }, function() { + if(vars.hover_pause == 'resume'){ + base.playToggle(); + vars.hover_pause = false; + } + }); + } + + if (base.options.slide_links){ + // Slide marker clicked + $(vars.slide_list+'> li').click(function(){ + + index = $(vars.slide_list+'> li').index(this); + targetSlide = index + 1; + + base.goTo(targetSlide); + return false; + + }); + } + + // Thumb marker clicked + if (base.options.thumb_links){ + $(vars.thumb_list+'> li').click(function(){ + + index = $(vars.thumb_list+'> li').index(this); + targetSlide = index + 1; + + api.goTo(targetSlide); + return false; + + }); + } + + // Start slideshow if enabled + if (base.options.slideshow && base.options.slides.length > 1){ + + // Start slideshow if autoplay enabled + if (base.options.autoplay && base.options.slides.length > 1){ + vars.slideshow_interval = setInterval(base.nextSlide, base.options.slide_interval); // Initiate slide interval + }else{ + vars.is_paused = true; // Mark as paused + } + + //Prevent navigation items from being dragged + $('.load-item img').bind("contextmenu mousedown",function(){ + return false; + }); + + } + + // Adjust image when browser is resized + $(window).resize(function(){ + base.resizeNow(); + }); + + }; + + + /* Resize Images + ----------------------------*/ + base.resizeNow = function(){ + + return base.$el.each(function() { + // Resize each image seperately + $('img', base.el).each(function(){ + + thisSlide = $(this); + var ratio = (thisSlide.data('origHeight')/thisSlide.data('origWidth')).toFixed(2); // Define image ratio + + // Gather browser size + var browserwidth = base.$el.width(), + browserheight = base.$el.height(), + offset; + + /*-----Resize Image-----*/ + if (base.options.fit_always){ // Fit always is enabled + if ((browserheight/browserwidth) > ratio){ + resizeWidth(); + } else { + resizeHeight(); + } + }else{ // Normal Resize + if ((browserheight <= base.options.min_height) && (browserwidth <= base.options.min_width)){ // If window smaller than minimum width and height + + if ((browserheight/browserwidth) > ratio){ + base.options.fit_landscape && ratio < 1 ? resizeWidth(true) : resizeHeight(true); // If landscapes are set to fit + } else { + base.options.fit_portrait && ratio >= 1 ? resizeHeight(true) : resizeWidth(true); // If portraits are set to fit + } + + } else if (browserwidth <= base.options.min_width){ // If window only smaller than minimum width + + if ((browserheight/browserwidth) > ratio){ + base.options.fit_landscape && ratio < 1 ? resizeWidth(true) : resizeHeight(); // If landscapes are set to fit + } else { + base.options.fit_portrait && ratio >= 1 ? resizeHeight() : resizeWidth(true); // If portraits are set to fit + } + + } else if (browserheight <= base.options.min_height){ // If window only smaller than minimum height + + if ((browserheight/browserwidth) > ratio){ + base.options.fit_landscape && ratio < 1 ? resizeWidth() : resizeHeight(true); // If landscapes are set to fit + } else { + base.options.fit_portrait && ratio >= 1 ? resizeHeight(true) : resizeWidth(); // If portraits are set to fit + } + + } else { // If larger than minimums + + if ((browserheight/browserwidth) > ratio){ + base.options.fit_landscape && ratio < 1 ? resizeWidth() : resizeHeight(); // If landscapes are set to fit + } else { + base.options.fit_portrait && ratio >= 1 ? resizeHeight() : resizeWidth(); // If portraits are set to fit + } + + } + } + /*-----End Image Resize-----*/ + + + /*-----Resize Functions-----*/ + + function resizeWidth(minimum){ + if (minimum){ // If minimum height needs to be considered + if(thisSlide.width() < browserwidth || thisSlide.width() < base.options.min_width ){ + if (thisSlide.width() * ratio >= base.options.min_height){ + thisSlide.width(base.options.min_width); + thisSlide.height(thisSlide.width() * ratio); + }else{ + resizeHeight(); + } + } + }else{ + if (base.options.min_height >= browserheight && !base.options.fit_landscape){ // If minimum height needs to be considered + if (browserwidth * ratio >= base.options.min_height || (browserwidth * ratio >= base.options.min_height && ratio <= 1)){ // If resizing would push below minimum height or image is a landscape + thisSlide.width(browserwidth); + thisSlide.height(browserwidth * ratio); + } else if (ratio > 1){ // Else the image is portrait + thisSlide.height(base.options.min_height); + thisSlide.width(thisSlide.height() / ratio); + } else if (thisSlide.width() < browserwidth) { + thisSlide.width(browserwidth); + thisSlide.height(thisSlide.width() * ratio); + } + }else{ // Otherwise, resize as normal + thisSlide.width(browserwidth); + thisSlide.height(browserwidth * ratio); + } + } + }; + + function resizeHeight(minimum){ + if (minimum){ // If minimum height needs to be considered + if(thisSlide.height() < browserheight){ + if (thisSlide.height() / ratio >= base.options.min_width){ + thisSlide.height(base.options.min_height); + thisSlide.width(thisSlide.height() / ratio); + }else{ + resizeWidth(true); + } + } + }else{ // Otherwise, resized as normal + if (base.options.min_width >= browserwidth){ // If minimum width needs to be considered + if (browserheight / ratio >= base.options.min_width || ratio > 1){ // If resizing would push below minimum width or image is a portrait + thisSlide.height(browserheight); + thisSlide.width(browserheight / ratio); + } else if (ratio <= 1){ // Else the image is landscape + thisSlide.width(base.options.min_width); + thisSlide.height(thisSlide.width() * ratio); + } + }else{ // Otherwise, resize as normal + thisSlide.height(browserheight); + thisSlide.width(browserheight / ratio); + } + } + }; + + /*-----End Resize Functions-----*/ + + if (thisSlide.parents('li').hasClass('image-loading')){ + $('.image-loading').removeClass('image-loading'); + } + + // Horizontally Center + if (base.options.horizontal_center){ + $(this).css('left', (browserwidth - $(this).width())/2); + } + + // Vertically Center + if (base.options.vertical_center){ + $(this).css('top', (browserheight - $(this).height())/2); + } + + }); + + // Basic image drag and right click protection + if (base.options.image_protect){ + + $('img', base.el).bind("contextmenu mousedown",function(){ + return false; + }); + + } + + return false; + + }); + + }; + + + /* Next Slide + ----------------------------*/ + base.nextSlide = function(){ + + if(vars.in_animation || !api.options.slideshow) return false; // Abort if currently animating + else vars.in_animation = true; // Otherwise set animation marker + + clearInterval(vars.slideshow_interval); // Stop slideshow + + var slides = base.options.slides, // Pull in slides array + liveslide = base.$el.find('.activeslide'); // Find active slide + $('.prevslide').removeClass('prevslide'); + liveslide.removeClass('activeslide').addClass('prevslide'); // Remove active class & update previous slide + + // Get the slide number of new slide + vars.current_slide + 1 == base.options.slides.length ? vars.current_slide = 0 : vars.current_slide++; + + var nextslide = $(base.el+' li:eq('+vars.current_slide+')'), + prevslide = base.$el.find('.prevslide'); + + // If hybrid mode is on drop quality for transition + if (base.options.performance == 1) base.$el.removeClass('quality').addClass('speed'); + + + /*-----Load Image-----*/ + + loadSlide = false; + + vars.current_slide == base.options.slides.length - 1 ? loadSlide = 0 : loadSlide = vars.current_slide + 1; // Determine next slide + + var targetList = base.el+' li:eq('+loadSlide+')'; + if (!$(targetList).html()){ + + // If links should open in new window + var linkTarget = base.options.new_window ? ' target="_blank"' : ''; + + imageLink = (base.options.slides[loadSlide].url) ? "href='" + base.options.slides[loadSlide].url + "'" : ""; // If link exists, build it + var img = $(''); + + img.appendTo(targetList).wrap('').parent().parent().addClass('image-loading').css('visibility','hidden'); + + img.load(function(){ + base._origDim($(this)); + base.resizeNow(); + }); // End Load + }; + + // Update thumbnails (if enabled) + if (base.options.thumbnail_navigation == 1){ + + // Load previous thumbnail + vars.current_slide - 1 < 0 ? prevThumb = base.options.slides.length - 1 : prevThumb = vars.current_slide - 1; + $(vars.prev_thumb).html($("").attr("src", base.options.slides[prevThumb].image)); + + // Load next thumbnail + nextThumb = loadSlide; + $(vars.next_thumb).html($("").attr("src", base.options.slides[nextThumb].image)); + + } + + + + /*-----End Load Image-----*/ + + + // Call theme function for before slide transition + if( typeof theme != 'undefined' && typeof theme.beforeAnimation == "function" ) theme.beforeAnimation('next'); + + //Update slide markers + if (base.options.slide_links){ + $('.current-slide').removeClass('current-slide'); + $(vars.slide_list +'> li' ).eq(vars.current_slide).addClass('current-slide'); + } + + nextslide.css('visibility','hidden').addClass('activeslide'); // Update active slide + + switch(base.options.transition){ + case 0: case 'none': // No transition + nextslide.css('visibility','visible'); vars.in_animation = false; base.afterAnimation(); + break; + case 1: case 'fade': // Fade + nextslide.animate({opacity : 0},0).css('visibility','visible').animate({opacity : 1, avoidTransforms : false}, base.options.transition_speed, function(){ base.afterAnimation(); }); + break; + case 2: case 'slideTop': // Slide Top + nextslide.animate({top : -base.$el.height()}, 0 ).css('visibility','visible').animate({ top:0, avoidTransforms : false }, base.options.transition_speed, function(){ base.afterAnimation(); }); + break; + case 3: case 'slideRight': // Slide Right + nextslide.animate({left : base.$el.width()}, 0 ).css('visibility','visible').animate({ left:0, avoidTransforms : false }, base.options.transition_speed, function(){ base.afterAnimation(); }); + break; + case 4: case 'slideBottom': // Slide Bottom + nextslide.animate({top : base.$el.height()}, 0 ).css('visibility','visible').animate({ top:0, avoidTransforms : false }, base.options.transition_speed, function(){ base.afterAnimation(); }); + break; + case 5: case 'slideLeft': // Slide Left + nextslide.animate({left : -base.$el.width()}, 0 ).css('visibility','visible').animate({ left:0, avoidTransforms : false }, base.options.transition_speed, function(){ base.afterAnimation(); }); + break; + case 6: case 'carouselRight': // Carousel Right + nextslide.animate({left : base.$el.width()}, 0 ).css('visibility','visible').animate({ left:0, avoidTransforms : false }, base.options.transition_speed, function(){ base.afterAnimation(); }); + liveslide.animate({ left: -base.$el.width(), avoidTransforms : false }, base.options.transition_speed ); + break; + case 7: case 'carouselLeft': // Carousel Left + nextslide.animate({left : -base.$el.width()}, 0 ).css('visibility','visible').animate({ left:0, avoidTransforms : false }, base.options.transition_speed, function(){ base.afterAnimation(); }); + liveslide.animate({ left: base.$el.width(), avoidTransforms : false }, base.options.transition_speed ); + break; + } + return false; + }; + + + /* Previous Slide + ----------------------------*/ + base.prevSlide = function(){ + + if(vars.in_animation || !api.options.slideshow) return false; // Abort if currently animating + else vars.in_animation = true; // Otherwise set animation marker + + clearInterval(vars.slideshow_interval); // Stop slideshow + + var slides = base.options.slides, // Pull in slides array + liveslide = base.$el.find('.activeslide'); // Find active slide + $('.prevslide').removeClass('prevslide'); + liveslide.removeClass('activeslide').addClass('prevslide'); // Remove active class & update previous slide + + // Get current slide number + vars.current_slide == 0 ? vars.current_slide = base.options.slides.length - 1 : vars.current_slide-- ; + + var nextslide = $(base.el+' li:eq('+vars.current_slide+')'), + prevslide = base.$el.find('.prevslide'); + + // If hybrid mode is on drop quality for transition + if (base.options.performance == 1) base.$el.removeClass('quality').addClass('speed'); + + + /*-----Load Image-----*/ + + loadSlide = vars.current_slide; + + var targetList = base.el+' li:eq('+loadSlide+')'; + if (!$(targetList).html()){ + // If links should open in new window + var linkTarget = base.options.new_window ? ' target="_blank"' : ''; + imageLink = (base.options.slides[loadSlide].url) ? "href='" + base.options.slides[loadSlide].url + "'" : ""; // If link exists, build it + var img = $(''); + + img.appendTo(targetList).wrap('').parent().parent().addClass('image-loading').css('visibility','hidden'); + + img.load(function(){ + base._origDim($(this)); + base.resizeNow(); + }); // End Load + }; + + // Update thumbnails (if enabled) + if (base.options.thumbnail_navigation == 1){ + + // Load previous thumbnail + //prevThumb = loadSlide; + loadSlide == 0 ? prevThumb = base.options.slides.length - 1 : prevThumb = loadSlide - 1; + $(vars.prev_thumb).html($("").attr("src", base.options.slides[prevThumb].image)); + + // Load next thumbnail + vars.current_slide == base.options.slides.length - 1 ? nextThumb = 0 : nextThumb = vars.current_slide + 1; + $(vars.next_thumb).html($("").attr("src", base.options.slides[nextThumb].image)); + } + + /*-----End Load Image-----*/ + + + // Call theme function for before slide transition + if( typeof theme != 'undefined' && typeof theme.beforeAnimation == "function" ) theme.beforeAnimation('prev'); + + //Update slide markers + if (base.options.slide_links){ + $('.current-slide').removeClass('current-slide'); + $(vars.slide_list +'> li' ).eq(vars.current_slide).addClass('current-slide'); + } + + nextslide.css('visibility','hidden').addClass('activeslide'); // Update active slide + + switch(base.options.transition){ + case 0: case 'none': // No transition + nextslide.css('visibility','visible'); vars.in_animation = false; base.afterAnimation(); + break; + case 1: case 'fade': // Fade + nextslide.animate({opacity : 0},0).css('visibility','visible').animate({opacity : 1, avoidTransforms : false}, base.options.transition_speed, function(){ base.afterAnimation(); }); + break; + case 2: case 'slideTop': // Slide Top (reverse) + nextslide.animate({top : base.$el.height()}, 0 ).css('visibility','visible').animate({ top:0, avoidTransforms : false }, base.options.transition_speed, function(){ base.afterAnimation(); }); + break; + case 3: case 'slideRight': // Slide Right (reverse) + nextslide.animate({left : -base.$el.width()}, 0 ).css('visibility','visible').animate({ left:0, avoidTransforms : false }, base.options.transition_speed, function(){ base.afterAnimation(); }); + break; + case 4: case 'slideBottom': // Slide Bottom (reverse) + nextslide.animate({top : -base.$el.height()}, 0 ).css('visibility','visible').animate({ top:0, avoidTransforms : false }, base.options.transition_speed, function(){ base.afterAnimation(); }); + break; + case 5: case 'slideLeft': // Slide Left (reverse) + nextslide.animate({left : base.$el.width()}, 0 ).css('visibility','visible').animate({ left:0, avoidTransforms : false }, base.options.transition_speed, function(){ base.afterAnimation(); }); + break; + case 6: case 'carouselRight': // Carousel Right (reverse) + nextslide.animate({left : -base.$el.width()}, 0 ).css('visibility','visible').animate({ left:0, avoidTransforms : false }, base.options.transition_speed, function(){ base.afterAnimation(); }); + liveslide.animate({left : 0}, 0 ).animate({ left: base.$el.width(), avoidTransforms : false}, base.options.transition_speed ); + break; + case 7: case 'carouselLeft': // Carousel Left (reverse) + nextslide.animate({left : base.$el.width()}, 0 ).css('visibility','visible').animate({ left:0, avoidTransforms : false }, base.options.transition_speed, function(){ base.afterAnimation(); }); + liveslide.animate({left : 0}, 0 ).animate({ left: -base.$el.width(), avoidTransforms : false }, base.options.transition_speed ); + break; + } + return false; + }; + + + /* Play/Pause Toggle + ----------------------------*/ + base.playToggle = function(){ + + if (vars.in_animation || !api.options.slideshow) return false; // Abort if currently animating + + if (vars.is_paused){ + + vars.is_paused = false; + + // Call theme function for play + if( typeof theme != 'undefined' && typeof theme.playToggle == "function" ) theme.playToggle('play'); + + // Resume slideshow + vars.slideshow_interval = setInterval(base.nextSlide, base.options.slide_interval); + + }else{ + + vars.is_paused = true; + + // Call theme function for pause + if( typeof theme != 'undefined' && typeof theme.playToggle == "function" ) theme.playToggle('pause'); + + // Stop slideshow + clearInterval(vars.slideshow_interval); + + } + + return false; + + }; + + + /* Go to specific slide + ----------------------------*/ + base.goTo = function(targetSlide){ + if (vars.in_animation || !api.options.slideshow) return false; // Abort if currently animating + + var totalSlides = base.options.slides.length; + + // If target outside range + if(targetSlide < 0){ + targetSlide = totalSlides; + }else if(targetSlide > totalSlides){ + targetSlide = 1; + } + targetSlide = totalSlides - targetSlide + 1; + + clearInterval(vars.slideshow_interval); // Stop slideshow, prevent buildup + + // Call theme function for goTo trigger + if (typeof theme != 'undefined' && typeof theme.goTo == "function" ) theme.goTo(); + + if (vars.current_slide == totalSlides - targetSlide){ + if(!(vars.is_paused)){ + vars.slideshow_interval = setInterval(base.nextSlide, base.options.slide_interval); + } + return false; + } + + // If ahead of current position + if(totalSlides - targetSlide > vars.current_slide ){ + + // Adjust for new next slide + vars.current_slide = totalSlides-targetSlide-1; + vars.update_images = 'next'; + base._placeSlide(vars.update_images); + + //Otherwise it's before current position + }else if(totalSlides - targetSlide < vars.current_slide){ + + // Adjust for new prev slide + vars.current_slide = totalSlides-targetSlide+1; + vars.update_images = 'prev'; + base._placeSlide(vars.update_images); + + } + + // set active markers + if (base.options.slide_links){ + $(vars.slide_list +'> .current-slide').removeClass('current-slide'); + $(vars.slide_list +'> li').eq((totalSlides-targetSlide)).addClass('current-slide'); + } + + if (base.options.thumb_links){ + $(vars.thumb_list +'> .current-thumb').removeClass('current-thumb'); + $(vars.thumb_list +'> li').eq((totalSlides-targetSlide)).addClass('current-thumb'); + } + + }; + + + /* Place Slide + ----------------------------*/ + base._placeSlide = function(place){ + + // If links should open in new window + var linkTarget = base.options.new_window ? ' target="_blank"' : ''; + + loadSlide = false; + + if (place == 'next'){ + + vars.current_slide == base.options.slides.length - 1 ? loadSlide = 0 : loadSlide = vars.current_slide + 1; // Determine next slide + + var targetList = base.el+' li:eq('+loadSlide+')'; + + if (!$(targetList).html()){ + // If links should open in new window + var linkTarget = base.options.new_window ? ' target="_blank"' : ''; + + imageLink = (base.options.slides[loadSlide].url) ? "href='" + base.options.slides[loadSlide].url + "'" : ""; // If link exists, build it + var img = $(''); + + img.appendTo(targetList).wrap('').parent().parent().addClass('image-loading').css('visibility','hidden'); + + img.load(function(){ + base._origDim($(this)); + base.resizeNow(); + }); // End Load + }; + + base.nextSlide(); + + }else if (place == 'prev'){ + + vars.current_slide - 1 < 0 ? loadSlide = base.options.slides.length - 1 : loadSlide = vars.current_slide - 1; // Determine next slide + + var targetList = base.el+' li:eq('+loadSlide+')'; + + if (!$(targetList).html()){ + // If links should open in new window + var linkTarget = base.options.new_window ? ' target="_blank"' : ''; + + imageLink = (base.options.slides[loadSlide].url) ? "href='" + base.options.slides[loadSlide].url + "'" : ""; // If link exists, build it + var img = $(''); + + img.appendTo(targetList).wrap('').parent().parent().addClass('image-loading').css('visibility','hidden'); + + img.load(function(){ + base._origDim($(this)); + base.resizeNow(); + }); // End Load + }; + base.prevSlide(); + } + + }; + + + /* Get Original Dimensions + ----------------------------*/ + base._origDim = function(targetSlide){ + targetSlide.data('origWidth', targetSlide.width()).data('origHeight', targetSlide.height()); + }; + + + /* After Slide Animation + ----------------------------*/ + base.afterAnimation = function(){ + + // If hybrid mode is on swap back to higher image quality + if (base.options.performance == 1){ + base.$el.removeClass('speed').addClass('quality'); + } + + // Update previous slide + if (vars.update_images){ + vars.current_slide - 1 < 0 ? setPrev = base.options.slides.length - 1 : setPrev = vars.current_slide-1; + vars.update_images = false; + $('.prevslide').removeClass('prevslide'); + $(base.el+' li:eq('+setPrev+')').addClass('prevslide'); + } + + vars.in_animation = false; + + // Resume slideshow + if (!vars.is_paused && base.options.slideshow){ + vars.slideshow_interval = setInterval(base.nextSlide, base.options.slide_interval); + if (base.options.stop_loop && vars.current_slide == base.options.slides.length - 1 ) base.playToggle(); + } + + // Call theme function for after slide transition + if (typeof theme != 'undefined' && typeof theme.afterAnimation == "function" ) theme.afterAnimation(); + + return false; + + }; + + base.getField = function(field){ + return base.options.slides[vars.current_slide][field]; + }; + + // Make it go! + base.init(); + }; + + + /* Global Variables + ----------------------------*/ + $.supersized.vars = { + + // Elements + thumb_tray : '#thumb-tray', // Thumbnail tray + thumb_list : '#thumb-list', // Thumbnail list + slide_list : '#slide-list', // Slide link list + + // Internal variables + current_slide : 0, // Current slide number + in_animation : false, // Prevents animations from stacking + is_paused : false, // Tracks paused on/off + hover_pause : false, // If slideshow is paused from hover + slideshow_interval : false, // Stores slideshow timer + update_images : false, // Trigger to update images after slide jump + options : {} // Stores assembled options list + + }; + + + /* Default Options + ----------------------------*/ + $.supersized.defaultOptions = { + + // Functionality + slideshow : 1, // Slideshow on/off + autoplay : 1, // Slideshow starts playing automatically + start_slide : 1, // Start slide (0 is random) + stop_loop : 0, // Stops slideshow on last slide + random : 0, // Randomize slide order (Ignores start slide) + slide_interval : 5000, // Length between transitions + transition : 1, // 0-None, 1-Fade, 2-Slide Top, 3-Slide Right, 4-Slide Bottom, 5-Slide Left, 6-Carousel Right, 7-Carousel Left + transition_speed : 750, // Speed of transition + new_window : 1, // Image links open in new window/tab + pause_hover : 0, // Pause slideshow on hover + keyboard_nav : 1, // Keyboard navigation on/off + performance : 1, // 0-Normal, 1-Hybrid speed/quality, 2-Optimizes image quality, 3-Optimizes transition speed // (Only works for Firefox/IE, not Webkit) + image_protect : 1, // Disables image dragging and right click with Javascript + + // Size & Position + fit_always : 0, // Image will never exceed browser width or height (Ignores min. dimensions) + fit_landscape : 0, // Landscape images will not exceed browser width + fit_portrait : 1, // Portrait images will not exceed browser height + min_width : 0, // Min width allowed (in pixels) + min_height : 0, // Min height allowed (in pixels) + horizontal_center : 1, // Horizontally center background + vertical_center : 1, // Vertically center background + + + // Components + slide_links : 1, // Individual links for each slide (Options: false, 'num', 'name', 'blank') + thumb_links : 1, // Individual thumb links for each slide + thumbnail_navigation : 0 // Thumbnail navigation + + }; + + $.fn.supersized = function(options){ + return this.each(function(){ + (new $.supersized(options)); + }); + }; + +})(jQuery); + diff --git a/apps/gallery/js/supersized.3.2.7.min.js b/apps/gallery/js/supersized.3.2.7.min.js new file mode 100644 index 0000000000..b9cea9cee1 --- /dev/null +++ b/apps/gallery/js/supersized.3.2.7.min.js @@ -0,0 +1,13 @@ +/* + + Supersized - Fullscreen Slideshow jQuery Plugin + Version : 3.2.7 + Site : www.buildinternet.com/project/supersized + + Author : Sam Dunn + Company : One Mighty Roar (www.onemightyroar.com) + License : MIT License / GPL License + +*/ + +(function(a){a(document).ready(function(){a("body").append('
          ')});a.supersized=function(b){var c="#supersized",d=this;d.$el=a(c);d.el=c;vars=a.supersized.vars;d.$el.data("supersized",d);api=d.$el.data("supersized");d.init=function(){a.supersized.vars=a.extend(a.supersized.vars,a.supersized.themeVars);a.supersized.vars.options=a.extend({},a.supersized.defaultOptions,a.supersized.themeOptions,b);d.options=a.supersized.vars.options;d._build()};d._build=function(){var g=0,e="",j="",h,f="",i;while(g<=d.options.slides.length-1){switch(d.options.slide_links){case"num":h=g;break;case"name":h=d.options.slides[g].title;break;case"blank":h="";break}e=e+'
        • ';if(g==d.options.start_slide-1){if(d.options.slide_links){j=j+'"}if(d.options.thumb_links){d.options.slides[g].thumb?i=d.options.slides[g].thumb:i=d.options.slides[g].image;f=f+'
        • '}}else{if(d.options.slide_links){j=j+'"}if(d.options.thumb_links){d.options.slides[g].thumb?i=d.options.slides[g].thumb:i=d.options.slides[g].image;f=f+'
        • '}}g++}if(d.options.slide_links){a(vars.slide_list).html(j)}if(d.options.thumb_links&&vars.thumb_tray.length){a(vars.thumb_tray).append('
            '+f+"
          ")}a(d.el).append(e);if(d.options.thumbnail_navigation){vars.current_slide-1<0?prevThumb=d.options.slides.length-1:prevThumb=vars.current_slide-1;a(vars.prev_thumb).show().html(a("").attr("src",d.options.slides[prevThumb].image));vars.current_slide==d.options.slides.length-1?nextThumb=0:nextThumb=vars.current_slide+1;a(vars.next_thumb).show().html(a("").attr("src",d.options.slides[nextThumb].image))}d._start()};d._start=function(){if(d.options.start_slide){vars.current_slide=d.options.start_slide-1}else{vars.current_slide=Math.floor(Math.random()*d.options.slides.length)}var o=d.options.new_window?' target="_blank"':"";if(d.options.performance==3){d.$el.addClass("speed")}else{if((d.options.performance==1)||(d.options.performance==2)){d.$el.addClass("quality")}}if(d.options.random){arr=d.options.slides;for(var h,m,k=arr.length;k;h=parseInt(Math.random()*k),m=arr[--k],arr[k]=arr[h],arr[h]=m){}d.options.slides=arr}if(d.options.slides.length>1){if(d.options.slides.length>2){vars.current_slide-1<0?loadPrev=d.options.slides.length-1:loadPrev=vars.current_slide-1;var g=(d.options.slides[loadPrev].url)?"href='"+d.options.slides[loadPrev].url+"'":"";var q=a('');var n=d.el+" li:eq("+loadPrev+")";q.appendTo(n).wrap("").parent().parent().addClass("image-loading prevslide");q.load(function(){a(this).data("origWidth",a(this).width()).data("origHeight",a(this).height());d.resizeNow()})}}else{d.options.slideshow=0}g=(api.getField("url"))?"href='"+api.getField("url")+"'":"";var l=a('');var f=d.el+" li:eq("+vars.current_slide+")";l.appendTo(f).wrap("").parent().parent().addClass("image-loading activeslide");l.load(function(){d._origDim(a(this));d.resizeNow();d.launch();if(typeof theme!="undefined"&&typeof theme._init=="function"){theme._init()}});if(d.options.slides.length>1){vars.current_slide==d.options.slides.length-1?loadNext=0:loadNext=vars.current_slide+1;g=(d.options.slides[loadNext].url)?"href='"+d.options.slides[loadNext].url+"'":"";var e=a('');var p=d.el+" li:eq("+loadNext+")";e.appendTo(p).wrap("").parent().parent().addClass("image-loading");e.load(function(){a(this).data("origWidth",a(this).width()).data("origHeight",a(this).height());d.resizeNow()})}d.$el.css("visibility","hidden");a(".load-item").hide()};d.launch=function(){d.$el.css("visibility","visible");a("#supersized-loader").remove();if(typeof theme!="undefined"&&typeof theme.beforeAnimation=="function"){theme.beforeAnimation("next")}a(".load-item").show();if(d.options.keyboard_nav){a(document.documentElement).keyup(function(e){if(vars.in_animation){return false}if((e.keyCode==37)||(e.keyCode==40)){clearInterval(vars.slideshow_interval);d.prevSlide()}else{if((e.keyCode==39)||(e.keyCode==38)){clearInterval(vars.slideshow_interval);d.nextSlide()}else{if(e.keyCode==32&&!vars.hover_pause){clearInterval(vars.slideshow_interval);d.playToggle()}}}})}if(d.options.slideshow&&d.options.pause_hover){a(d.el).hover(function(){if(vars.in_animation){return false}vars.hover_pause=true;if(!vars.is_paused){vars.hover_pause="resume";d.playToggle()}},function(){if(vars.hover_pause=="resume"){d.playToggle();vars.hover_pause=false}})}if(d.options.slide_links){a(vars.slide_list+"> li").click(function(){index=a(vars.slide_list+"> li").index(this);targetSlide=index+1;d.goTo(targetSlide);return false})}if(d.options.thumb_links){a(vars.thumb_list+"> li").click(function(){index=a(vars.thumb_list+"> li").index(this);targetSlide=index+1;api.goTo(targetSlide);return false})}if(d.options.slideshow&&d.options.slides.length>1){if(d.options.autoplay&&d.options.slides.length>1){vars.slideshow_interval=setInterval(d.nextSlide,d.options.slide_interval)}else{vars.is_paused=true}a(".load-item img").bind("contextmenu mousedown",function(){return false})}a(window).resize(function(){d.resizeNow()})};d.resizeNow=function(){return d.$el.each(function(){a("img",d.el).each(function(){thisSlide=a(this);var f=(thisSlide.data("origHeight")/thisSlide.data("origWidth")).toFixed(2);var e=d.$el.width(),h=d.$el.height(),i;if(d.options.fit_always){if((h/e)>f){g()}else{j()}}else{if((h<=d.options.min_height)&&(e<=d.options.min_width)){if((h/e)>f){d.options.fit_landscape&&f<1?g(true):j(true)}else{d.options.fit_portrait&&f>=1?j(true):g(true)}}else{if(e<=d.options.min_width){if((h/e)>f){d.options.fit_landscape&&f<1?g(true):j()}else{d.options.fit_portrait&&f>=1?j():g(true)}}else{if(h<=d.options.min_height){if((h/e)>f){d.options.fit_landscape&&f<1?g():j(true)}else{d.options.fit_portrait&&f>=1?j(true):g()}}else{if((h/e)>f){d.options.fit_landscape&&f<1?g():j()}else{d.options.fit_portrait&&f>=1?j():g()}}}}}function g(k){if(k){if(thisSlide.width()=d.options.min_height){thisSlide.width(d.options.min_width);thisSlide.height(thisSlide.width()*f)}else{j()}}}else{if(d.options.min_height>=h&&!d.options.fit_landscape){if(e*f>=d.options.min_height||(e*f>=d.options.min_height&&f<=1)){thisSlide.width(e);thisSlide.height(e*f)}else{if(f>1){thisSlide.height(d.options.min_height);thisSlide.width(thisSlide.height()/f)}else{if(thisSlide.width()=d.options.min_width){thisSlide.height(d.options.min_height);thisSlide.width(thisSlide.height()/f)}else{g(true)}}}else{if(d.options.min_width>=e){if(h/f>=d.options.min_width||f>1){thisSlide.height(h);thisSlide.width(h/f)}else{if(f<=1){thisSlide.width(d.options.min_width);thisSlide.height(thisSlide.width()*f)}}}else{thisSlide.height(h);thisSlide.width(h/f)}}}if(thisSlide.parents("li").hasClass("image-loading")){a(".image-loading").removeClass("image-loading")}if(d.options.horizontal_center){a(this).css("left",(e-a(this).width())/2)}if(d.options.vertical_center){a(this).css("top",(h-a(this).height())/2)}});if(d.options.image_protect){a("img",d.el).bind("contextmenu mousedown",function(){return false})}return false})};d.nextSlide=function(){if(vars.in_animation||!api.options.slideshow){return false}else{vars.in_animation=true}clearInterval(vars.slideshow_interval);var h=d.options.slides,e=d.$el.find(".activeslide");a(".prevslide").removeClass("prevslide");e.removeClass("activeslide").addClass("prevslide");vars.current_slide+1==d.options.slides.length?vars.current_slide=0:vars.current_slide++;var g=a(d.el+" li:eq("+vars.current_slide+")"),i=d.$el.find(".prevslide");if(d.options.performance==1){d.$el.removeClass("quality").addClass("speed")}loadSlide=false;vars.current_slide==d.options.slides.length-1?loadSlide=0:loadSlide=vars.current_slide+1;var k=d.el+" li:eq("+loadSlide+")";if(!a(k).html()){var j=d.options.new_window?' target="_blank"':"";imageLink=(d.options.slides[loadSlide].url)?"href='"+d.options.slides[loadSlide].url+"'":"";var f=a('');f.appendTo(k).wrap("").parent().parent().addClass("image-loading").css("visibility","hidden");f.load(function(){d._origDim(a(this));d.resizeNow()})}if(d.options.thumbnail_navigation==1){vars.current_slide-1<0?prevThumb=d.options.slides.length-1:prevThumb=vars.current_slide-1;a(vars.prev_thumb).html(a("").attr("src",d.options.slides[prevThumb].image));nextThumb=loadSlide;a(vars.next_thumb).html(a("").attr("src",d.options.slides[nextThumb].image))}if(typeof theme!="undefined"&&typeof theme.beforeAnimation=="function"){theme.beforeAnimation("next")}if(d.options.slide_links){a(".current-slide").removeClass("current-slide");a(vars.slide_list+"> li").eq(vars.current_slide).addClass("current-slide")}g.css("visibility","hidden").addClass("activeslide");switch(d.options.transition){case 0:case"none":g.css("visibility","visible");vars.in_animation=false;d.afterAnimation();break;case 1:case"fade":g.animate({opacity:0},0).css("visibility","visible").animate({opacity:1,avoidTransforms:false},d.options.transition_speed,function(){d.afterAnimation()});break;case 2:case"slideTop":g.animate({top:-d.$el.height()},0).css("visibility","visible").animate({top:0,avoidTransforms:false},d.options.transition_speed,function(){d.afterAnimation()});break;case 3:case"slideRight":g.animate({left:d.$el.width()},0).css("visibility","visible").animate({left:0,avoidTransforms:false},d.options.transition_speed,function(){d.afterAnimation()});break;case 4:case"slideBottom":g.animate({top:d.$el.height()},0).css("visibility","visible").animate({top:0,avoidTransforms:false},d.options.transition_speed,function(){d.afterAnimation()});break;case 5:case"slideLeft":g.animate({left:-d.$el.width()},0).css("visibility","visible").animate({left:0,avoidTransforms:false},d.options.transition_speed,function(){d.afterAnimation()});break;case 6:case"carouselRight":g.animate({left:d.$el.width()},0).css("visibility","visible").animate({left:0,avoidTransforms:false},d.options.transition_speed,function(){d.afterAnimation()});e.animate({left:-d.$el.width(),avoidTransforms:false},d.options.transition_speed);break;case 7:case"carouselLeft":g.animate({left:-d.$el.width()},0).css("visibility","visible").animate({left:0,avoidTransforms:false},d.options.transition_speed,function(){d.afterAnimation()});e.animate({left:d.$el.width(),avoidTransforms:false},d.options.transition_speed);break}return false};d.prevSlide=function(){if(vars.in_animation||!api.options.slideshow){return false}else{vars.in_animation=true}clearInterval(vars.slideshow_interval);var h=d.options.slides,e=d.$el.find(".activeslide");a(".prevslide").removeClass("prevslide");e.removeClass("activeslide").addClass("prevslide");vars.current_slide==0?vars.current_slide=d.options.slides.length-1:vars.current_slide--;var g=a(d.el+" li:eq("+vars.current_slide+")"),i=d.$el.find(".prevslide");if(d.options.performance==1){d.$el.removeClass("quality").addClass("speed")}loadSlide=vars.current_slide;var k=d.el+" li:eq("+loadSlide+")";if(!a(k).html()){var j=d.options.new_window?' target="_blank"':"";imageLink=(d.options.slides[loadSlide].url)?"href='"+d.options.slides[loadSlide].url+"'":"";var f=a('');f.appendTo(k).wrap("").parent().parent().addClass("image-loading").css("visibility","hidden");f.load(function(){d._origDim(a(this));d.resizeNow()})}if(d.options.thumbnail_navigation==1){loadSlide==0?prevThumb=d.options.slides.length-1:prevThumb=loadSlide-1;a(vars.prev_thumb).html(a("").attr("src",d.options.slides[prevThumb].image));vars.current_slide==d.options.slides.length-1?nextThumb=0:nextThumb=vars.current_slide+1;a(vars.next_thumb).html(a("").attr("src",d.options.slides[nextThumb].image))}if(typeof theme!="undefined"&&typeof theme.beforeAnimation=="function"){theme.beforeAnimation("prev")}if(d.options.slide_links){a(".current-slide").removeClass("current-slide");a(vars.slide_list+"> li").eq(vars.current_slide).addClass("current-slide")}g.css("visibility","hidden").addClass("activeslide");switch(d.options.transition){case 0:case"none":g.css("visibility","visible");vars.in_animation=false;d.afterAnimation();break;case 1:case"fade":g.animate({opacity:0},0).css("visibility","visible").animate({opacity:1,avoidTransforms:false},d.options.transition_speed,function(){d.afterAnimation()});break;case 2:case"slideTop":g.animate({top:d.$el.height()},0).css("visibility","visible").animate({top:0,avoidTransforms:false},d.options.transition_speed,function(){d.afterAnimation()});break;case 3:case"slideRight":g.animate({left:-d.$el.width()},0).css("visibility","visible").animate({left:0,avoidTransforms:false},d.options.transition_speed,function(){d.afterAnimation()});break;case 4:case"slideBottom":g.animate({top:-d.$el.height()},0).css("visibility","visible").animate({top:0,avoidTransforms:false},d.options.transition_speed,function(){d.afterAnimation()});break;case 5:case"slideLeft":g.animate({left:d.$el.width()},0).css("visibility","visible").animate({left:0,avoidTransforms:false},d.options.transition_speed,function(){d.afterAnimation()});break;case 6:case"carouselRight":g.animate({left:-d.$el.width()},0).css("visibility","visible").animate({left:0,avoidTransforms:false},d.options.transition_speed,function(){d.afterAnimation()});e.animate({left:0},0).animate({left:d.$el.width(),avoidTransforms:false},d.options.transition_speed);break;case 7:case"carouselLeft":g.animate({left:d.$el.width()},0).css("visibility","visible").animate({left:0,avoidTransforms:false},d.options.transition_speed,function(){d.afterAnimation()});e.animate({left:0},0).animate({left:-d.$el.width(),avoidTransforms:false},d.options.transition_speed);break}return false};d.playToggle=function(){if(vars.in_animation||!api.options.slideshow){return false}if(vars.is_paused){vars.is_paused=false;if(typeof theme!="undefined"&&typeof theme.playToggle=="function"){theme.playToggle("play")}vars.slideshow_interval=setInterval(d.nextSlide,d.options.slide_interval)}else{vars.is_paused=true;if(typeof theme!="undefined"&&typeof theme.playToggle=="function"){theme.playToggle("pause")}clearInterval(vars.slideshow_interval)}return false};d.goTo=function(f){if(vars.in_animation||!api.options.slideshow){return false}var e=d.options.slides.length;if(f<0){f=e}else{if(f>e){f=1}}f=e-f+1;clearInterval(vars.slideshow_interval);if(typeof theme!="undefined"&&typeof theme.goTo=="function"){theme.goTo()}if(vars.current_slide==e-f){if(!(vars.is_paused)){vars.slideshow_interval=setInterval(d.nextSlide,d.options.slide_interval)}return false}if(e-f>vars.current_slide){vars.current_slide=e-f-1;vars.update_images="next";d._placeSlide(vars.update_images)}else{if(e-f .current-slide").removeClass("current-slide");a(vars.slide_list+"> li").eq((e-f)).addClass("current-slide")}if(d.options.thumb_links){a(vars.thumb_list+"> .current-thumb").removeClass("current-thumb");a(vars.thumb_list+"> li").eq((e-f)).addClass("current-thumb")}};d._placeSlide=function(e){var h=d.options.new_window?' target="_blank"':"";loadSlide=false;if(e=="next"){vars.current_slide==d.options.slides.length-1?loadSlide=0:loadSlide=vars.current_slide+1;var g=d.el+" li:eq("+loadSlide+")";if(!a(g).html()){var h=d.options.new_window?' target="_blank"':"";imageLink=(d.options.slides[loadSlide].url)?"href='"+d.options.slides[loadSlide].url+"'":"";var f=a('');f.appendTo(g).wrap("").parent().parent().addClass("image-loading").css("visibility","hidden");f.load(function(){d._origDim(a(this));d.resizeNow()})}d.nextSlide()}else{if(e=="prev"){vars.current_slide-1<0?loadSlide=d.options.slides.length-1:loadSlide=vars.current_slide-1;var g=d.el+" li:eq("+loadSlide+")";if(!a(g).html()){var h=d.options.new_window?' target="_blank"':"";imageLink=(d.options.slides[loadSlide].url)?"href='"+d.options.slides[loadSlide].url+"'":"";var f=a('');f.appendTo(g).wrap("").parent().parent().addClass("image-loading").css("visibility","hidden");f.load(function(){d._origDim(a(this));d.resizeNow()})}d.prevSlide()}}};d._origDim=function(e){e.data("origWidth",e.width()).data("origHeight",e.height())};d.afterAnimation=function(){if(d.options.performance==1){d.$el.removeClass("speed").addClass("quality")}if(vars.update_images){vars.current_slide-1<0?setPrev=d.options.slides.length-1:setPrev=vars.current_slide-1;vars.update_images=false;a(".prevslide").removeClass("prevslide");a(d.el+" li:eq("+setPrev+")").addClass("prevslide")}vars.in_animation=false;if(!vars.is_paused&&d.options.slideshow){vars.slideshow_interval=setInterval(d.nextSlide,d.options.slide_interval);if(d.options.stop_loop&&vars.current_slide==d.options.slides.length-1){d.playToggle()}}if(typeof theme!="undefined"&&typeof theme.afterAnimation=="function"){theme.afterAnimation()}return false};d.getField=function(e){return d.options.slides[vars.current_slide][e]};d.init()};a.supersized.vars={thumb_tray:"#thumb-tray",thumb_list:"#thumb-list",slide_list:"#slide-list",current_slide:0,in_animation:false,is_paused:false,hover_pause:false,slideshow_interval:false,update_images:false,options:{}};a.supersized.defaultOptions={slideshow:1,autoplay:1,start_slide:1,stop_loop:0,random:0,slide_interval:5000,transition:1,transition_speed:750,new_window:1,pause_hover:0,keyboard_nav:1,performance:1,image_protect:1,fit_always:0,fit_landscape:0,fit_portrait:1,min_width:0,min_height:0,horizontal_center:1,vertical_center:1,slide_links:1,thumb_links:1,thumbnail_navigation:0};a.fn.supersized=function(b){return this.each(function(){(new a.supersized(b))})}})(jQuery); \ No newline at end of file diff --git a/apps/gallery/js/supersized.shutter.js b/apps/gallery/js/supersized.shutter.js new file mode 100644 index 0000000000..cc3025a94a --- /dev/null +++ b/apps/gallery/js/supersized.shutter.js @@ -0,0 +1,337 @@ +/* + + Supersized - Fullscreen Slideshow jQuery Plugin + Version : 3.2.7 + Theme : Shutter 1.1 + + Site : www.buildinternet.com/project/supersized + Author : Sam Dunn + Company : One Mighty Roar (www.onemightyroar.com) + License : MIT License / GPL License + +*/ + +(function($){ + + theme = { + + + /* Initial Placement + ----------------------------*/ + _init : function(){ + + // Center Slide Links + if (api.options.slide_links) $(vars.slide_list).css('margin-left', -$(vars.slide_list).width()/2); + + // Start progressbar if autoplay enabled + if (api.options.autoplay){ + if (api.options.progress_bar) theme.progressBar(); + }else{ + if ($(vars.play_button).attr('src')) $(vars.play_button).attr("src", vars.image_path + "play.png"); // If pause play button is image, swap src + if (api.options.progress_bar) $(vars.progress_bar).stop().animate({left : -$(window).width()}, 0 ); // Place progress bar + } + + + /* Thumbnail Tray + ----------------------------*/ + // Hide tray off screen + $(vars.thumb_tray).animate({bottom : -$(vars.thumb_tray).height()}, 0 ); + + // Thumbnail Tray Toggle + $(vars.tray_button).toggle(function(){ + $(vars.thumb_tray).stop().animate({bottom : 0, avoidTransforms : true}, 300 ); + if ($(vars.tray_arrow).attr('src')) $(vars.tray_arrow).attr("src", vars.image_path + "button-tray-down.png"); + return false; + }, function() { + $(vars.thumb_tray).stop().animate({bottom : -$(vars.thumb_tray).height(), avoidTransforms : true}, 300 ); + if ($(vars.tray_arrow).attr('src')) $(vars.tray_arrow).attr("src", vars.image_path + "button-tray-up.png"); + return false; + }); + + // Make thumb tray proper size + $(vars.thumb_list).width($('> li', vars.thumb_list).length * $('> li', vars.thumb_list).outerWidth(true)); //Adjust to true width of thumb markers + + // Display total slides + if ($(vars.slide_total).length){ + $(vars.slide_total).html(api.options.slides.length); + } + + + /* Thumbnail Tray Navigation + ----------------------------*/ + if (api.options.thumb_links){ + //Hide thumb arrows if not needed + if ($(vars.thumb_list).width() <= $(vars.thumb_tray).width()){ + $(vars.thumb_back +','+vars.thumb_forward).fadeOut(0); + } + + // Thumb Intervals + vars.thumb_interval = Math.floor($(vars.thumb_tray).width() / $('> li', vars.thumb_list).outerWidth(true)) * $('> li', vars.thumb_list).outerWidth(true); + vars.thumb_page = 0; + + // Cycle thumbs forward + $(vars.thumb_forward).click(function(){ + if (vars.thumb_page - vars.thumb_interval <= -$(vars.thumb_list).width()){ + vars.thumb_page = 0; + $(vars.thumb_list).stop().animate({'left': vars.thumb_page}, {duration:500, easing:'easeOutExpo'}); + }else{ + vars.thumb_page = vars.thumb_page - vars.thumb_interval; + $(vars.thumb_list).stop().animate({'left': vars.thumb_page}, {duration:500, easing:'easeOutExpo'}); + } + }); + + // Cycle thumbs backwards + $(vars.thumb_back).click(function(){ + if (vars.thumb_page + vars.thumb_interval > 0){ + vars.thumb_page = Math.floor($(vars.thumb_list).width() / vars.thumb_interval) * -vars.thumb_interval; + if ($(vars.thumb_list).width() <= -vars.thumb_page) vars.thumb_page = vars.thumb_page + vars.thumb_interval; + $(vars.thumb_list).stop().animate({'left': vars.thumb_page}, {duration:500, easing:'easeOutExpo'}); + }else{ + vars.thumb_page = vars.thumb_page + vars.thumb_interval; + $(vars.thumb_list).stop().animate({'left': vars.thumb_page}, {duration:500, easing:'easeOutExpo'}); + } + }); + + } + + + /* Navigation Items + ----------------------------*/ + $(vars.next_slide).click(function() { + api.nextSlide(); + }); + + $(vars.prev_slide).click(function() { + api.prevSlide(); + }); + + // Full Opacity on Hover + if(jQuery.support.opacity){ + $(vars.prev_slide +','+vars.next_slide).mouseover(function() { + $(this).stop().animate({opacity:1},100); + }).mouseout(function(){ + $(this).stop().animate({opacity:0.6},100); + }); + } + + if (api.options.thumbnail_navigation){ + // Next thumbnail clicked + $(vars.next_thumb).click(function() { + api.nextSlide(); + }); + // Previous thumbnail clicked + $(vars.prev_thumb).click(function() { + api.prevSlide(); + }); + } + + $(vars.play_button).click(function() { + api.playToggle(); + }); + + + /* Thumbnail Mouse Scrub + ----------------------------*/ + if (api.options.mouse_scrub){ + $(vars.thumb_tray).mousemove(function(e) { + var containerWidth = $(vars.thumb_tray).width(), + listWidth = $(vars.thumb_list).width(); + if (listWidth > containerWidth){ + var mousePos = 1, + diff = e.pageX - mousePos; + if (diff > 10 || diff < -10) { + mousePos = e.pageX; + newX = (containerWidth - listWidth) * (e.pageX/containerWidth); + diff = parseInt(Math.abs(parseInt($(vars.thumb_list).css('left'))-newX )).toFixed(0); + $(vars.thumb_list).stop().animate({'left':newX}, {duration:diff*3, easing:'easeOutExpo'}); + } + } + }); + } + + + /* Window Resize + ----------------------------*/ + $(window).resize(function(){ + + // Delay progress bar on resize + if (api.options.progress_bar && !vars.in_animation){ + if (vars.slideshow_interval) clearInterval(vars.slideshow_interval); + if (api.options.slides.length - 1 > 0) clearInterval(vars.slideshow_interval); + + $(vars.progress_bar).stop().animate({left : -$(window).width()}, 0 ); + + if (!vars.progressDelay && api.options.slideshow){ + // Delay slideshow from resuming so Chrome can refocus images + vars.progressDelay = setTimeout(function() { + if (!vars.is_paused){ + theme.progressBar(); + vars.slideshow_interval = setInterval(api.nextSlide, api.options.slide_interval); + } + vars.progressDelay = false; + }, 1000); + } + } + + // Thumb Links + if (api.options.thumb_links && vars.thumb_tray.length){ + // Update Thumb Interval & Page + vars.thumb_page = 0; + vars.thumb_interval = Math.floor($(vars.thumb_tray).width() / $('> li', vars.thumb_list).outerWidth(true)) * $('> li', vars.thumb_list).outerWidth(true); + + // Adjust thumbnail markers + if ($(vars.thumb_list).width() > $(vars.thumb_tray).width()){ + $(vars.thumb_back +','+vars.thumb_forward).fadeIn('fast'); + $(vars.thumb_list).stop().animate({'left':0}, 200); + }else{ + $(vars.thumb_back +','+vars.thumb_forward).fadeOut('fast'); + } + + } + }); + + + }, + + + /* Go To Slide + ----------------------------*/ + goTo : function(){ + if (api.options.progress_bar && !vars.is_paused){ + $(vars.progress_bar).stop().animate({left : -$(window).width()}, 0 ); + theme.progressBar(); + } + }, + + /* Play & Pause Toggle + ----------------------------*/ + playToggle : function(state){ + + if (state =='play'){ + // If image, swap to pause + if ($(vars.play_button).attr('src')) $(vars.play_button).attr("src", vars.image_path + "pause.png"); + if (api.options.progress_bar && !vars.is_paused) theme.progressBar(); + }else if (state == 'pause'){ + // If image, swap to play + if ($(vars.play_button).attr('src')) $(vars.play_button).attr("src", vars.image_path + "play.png"); + if (api.options.progress_bar && vars.is_paused)$(vars.progress_bar).stop().animate({left : -$(window).width()}, 0 ); + } + + }, + + + /* Before Slide Transition + ----------------------------*/ + beforeAnimation : function(direction){ + if (api.options.progress_bar && !vars.is_paused) $(vars.progress_bar).stop().animate({left : -$(window).width()}, 0 ); + + /* Update Fields + ----------------------------*/ + // Update slide caption + if ($(vars.slide_caption).length){ + (api.getField('title')) ? $(vars.slide_caption).html(api.getField('title')) : $(vars.slide_caption).html(''); + } + // Update slide number + if (vars.slide_current.length){ + $(vars.slide_current).html(vars.current_slide + 1); + } + + + // Highlight current thumbnail and adjust row position + if (api.options.thumb_links){ + + $('.current-thumb').removeClass('current-thumb'); + $('li', vars.thumb_list).eq(vars.current_slide).addClass('current-thumb'); + + // If thumb out of view + if ($(vars.thumb_list).width() > $(vars.thumb_tray).width()){ + // If next slide direction + if (direction == 'next'){ + if (vars.current_slide == 0){ + vars.thumb_page = 0; + $(vars.thumb_list).stop().animate({'left': vars.thumb_page}, {duration:500, easing:'easeOutExpo'}); + } else if ($('.current-thumb').offset().left - $(vars.thumb_tray).offset().left >= vars.thumb_interval){ + vars.thumb_page = vars.thumb_page - vars.thumb_interval; + $(vars.thumb_list).stop().animate({'left': vars.thumb_page}, {duration:500, easing:'easeOutExpo'}); + } + // If previous slide direction + }else if(direction == 'prev'){ + if (vars.current_slide == api.options.slides.length - 1){ + vars.thumb_page = Math.floor($(vars.thumb_list).width() / vars.thumb_interval) * -vars.thumb_interval; + if ($(vars.thumb_list).width() <= -vars.thumb_page) vars.thumb_page = vars.thumb_page + vars.thumb_interval; + $(vars.thumb_list).stop().animate({'left': vars.thumb_page}, {duration:500, easing:'easeOutExpo'}); + } else if ($('.current-thumb').offset().left - $(vars.thumb_tray).offset().left < 0){ + if (vars.thumb_page + vars.thumb_interval > 0) return false; + vars.thumb_page = vars.thumb_page + vars.thumb_interval; + $(vars.thumb_list).stop().animate({'left': vars.thumb_page}, {duration:500, easing:'easeOutExpo'}); + } + } + } + + + } + + }, + + + /* After Slide Transition + ----------------------------*/ + afterAnimation : function(){ + if (api.options.progress_bar && !vars.is_paused) theme.progressBar(); // Start progress bar + }, + + + /* Progress Bar + ----------------------------*/ + progressBar : function(){ + $(vars.progress_bar).stop().animate({left : -$(window).width()}, 0 ).animate({left:0}, api.options.slide_interval); + } + + + }; + + + /* Theme Specific Variables + ----------------------------*/ + $.supersized.themeVars = { + + // Internal Variables + progress_delay : false, // Delay after resize before resuming slideshow + thumb_page : false, // Thumbnail page + thumb_interval : false, // Thumbnail interval + image_path : OC.webroot+"/apps/gallery/img/supersized/", // Default image path + + // General Elements + play_button : '#pauseplay', // Play/Pause button + next_slide : '#nextslide', // Next slide button + prev_slide : '#prevslide', // Prev slide button + next_thumb : '#nextthumb', // Next slide thumb button + prev_thumb : '#prevthumb', // Prev slide thumb button + + slide_caption : '#slidecaption', // Slide caption + slide_current : '.slidenumber', // Current slide number + slide_total : '.totalslides', // Total Slides + slide_list : '#slide-list', // Slide jump list + + thumb_tray : '#thumb-tray', // Thumbnail tray + thumb_list : '#thumb-list', // Thumbnail list + thumb_forward : '#thumb-forward', // Cycles forward through thumbnail list + thumb_back : '#thumb-back', // Cycles backwards through thumbnail list + tray_arrow : '#tray-arrow', // Thumbnail tray button arrow + tray_button : '#tray-button', // Thumbnail tray button + + progress_bar : '#progress-bar' // Progress bar + + }; + + /* Theme Specific Options + ----------------------------*/ + $.supersized.themeOptions = { + + progress_bar : 1, // Timer for each slide + mouse_scrub : 0 // Thumbnails move with mouse + + }; + + +})(jQuery); diff --git a/apps/gallery/js/supersized.shutter.min.js b/apps/gallery/js/supersized.shutter.min.js new file mode 100644 index 0000000000..52ea4a3384 --- /dev/null +++ b/apps/gallery/js/supersized.shutter.min.js @@ -0,0 +1,14 @@ +/* + + Supersized - Fullscreen Slideshow jQuery Plugin + Version : 3.2.7 + Theme : Shutter 1.1 + + Site : www.buildinternet.com/project/supersized + Author : Sam Dunn + Company : One Mighty Roar (www.onemightyroar.com) + License : MIT License / GPL License + +*/ + +(function(a){theme={_init:function(){if(api.options.slide_links){a(vars.slide_list).css("margin-left",-a(vars.slide_list).width()/2)}if(api.options.autoplay){if(api.options.progress_bar){theme.progressBar()}}else{if(a(vars.play_button).attr("src")){a(vars.play_button).attr("src",vars.image_path+"play.png")}if(api.options.progress_bar){a(vars.progress_bar).stop().animate({left:-a(window).width()},0)}}a(vars.thumb_tray).animate({bottom:-a(vars.thumb_tray).height()},0);a(vars.tray_button).toggle(function(){a(vars.thumb_tray).stop().animate({bottom:0,avoidTransforms:true},300);if(a(vars.tray_arrow).attr("src")){a(vars.tray_arrow).attr("src",vars.image_path+"button-tray-down.png")}return false},function(){a(vars.thumb_tray).stop().animate({bottom:-a(vars.thumb_tray).height(),avoidTransforms:true},300);if(a(vars.tray_arrow).attr("src")){a(vars.tray_arrow).attr("src",vars.image_path+"button-tray-up.png")}return false});a(vars.thumb_list).width(a("> li",vars.thumb_list).length*a("> li",vars.thumb_list).outerWidth(true));if(a(vars.slide_total).length){a(vars.slide_total).html(api.options.slides.length)}if(api.options.thumb_links){if(a(vars.thumb_list).width()<=a(vars.thumb_tray).width()){a(vars.thumb_back+","+vars.thumb_forward).fadeOut(0)}vars.thumb_interval=Math.floor(a(vars.thumb_tray).width()/a("> li",vars.thumb_list).outerWidth(true))*a("> li",vars.thumb_list).outerWidth(true);vars.thumb_page=0;a(vars.thumb_forward).click(function(){if(vars.thumb_page-vars.thumb_interval<=-a(vars.thumb_list).width()){vars.thumb_page=0;a(vars.thumb_list).stop().animate({left:vars.thumb_page},{duration:500,easing:"easeOutExpo"})}else{vars.thumb_page=vars.thumb_page-vars.thumb_interval;a(vars.thumb_list).stop().animate({left:vars.thumb_page},{duration:500,easing:"easeOutExpo"})}});a(vars.thumb_back).click(function(){if(vars.thumb_page+vars.thumb_interval>0){vars.thumb_page=Math.floor(a(vars.thumb_list).width()/vars.thumb_interval)*-vars.thumb_interval;if(a(vars.thumb_list).width()<=-vars.thumb_page){vars.thumb_page=vars.thumb_page+vars.thumb_interval}a(vars.thumb_list).stop().animate({left:vars.thumb_page},{duration:500,easing:"easeOutExpo"})}else{vars.thumb_page=vars.thumb_page+vars.thumb_interval;a(vars.thumb_list).stop().animate({left:vars.thumb_page},{duration:500,easing:"easeOutExpo"})}})}a(vars.next_slide).click(function(){api.nextSlide()});a(vars.prev_slide).click(function(){api.prevSlide()});if(jQuery.support.opacity){a(vars.prev_slide+","+vars.next_slide).mouseover(function(){a(this).stop().animate({opacity:1},100)}).mouseout(function(){a(this).stop().animate({opacity:0.6},100)})}if(api.options.thumbnail_navigation){a(vars.next_thumb).click(function(){api.nextSlide()});a(vars.prev_thumb).click(function(){api.prevSlide()})}a(vars.play_button).click(function(){api.playToggle()});if(api.options.mouse_scrub){a(vars.thumb_tray).mousemove(function(f){var c=a(vars.thumb_tray).width(),g=a(vars.thumb_list).width();if(g>c){var b=1,d=f.pageX-b;if(d>10||d<-10){b=f.pageX;newX=(c-g)*(f.pageX/c);d=parseInt(Math.abs(parseInt(a(vars.thumb_list).css("left"))-newX)).toFixed(0);a(vars.thumb_list).stop().animate({left:newX},{duration:d*3,easing:"easeOutExpo"})}}})}a(window).resize(function(){if(api.options.progress_bar&&!vars.in_animation){if(vars.slideshow_interval){clearInterval(vars.slideshow_interval)}if(api.options.slides.length-1>0){clearInterval(vars.slideshow_interval)}a(vars.progress_bar).stop().animate({left:-a(window).width()},0);if(!vars.progressDelay&&api.options.slideshow){vars.progressDelay=setTimeout(function(){if(!vars.is_paused){theme.progressBar();vars.slideshow_interval=setInterval(api.nextSlide,api.options.slide_interval)}vars.progressDelay=false},1000)}}if(api.options.thumb_links&&vars.thumb_tray.length){vars.thumb_page=0;vars.thumb_interval=Math.floor(a(vars.thumb_tray).width()/a("> li",vars.thumb_list).outerWidth(true))*a("> li",vars.thumb_list).outerWidth(true);if(a(vars.thumb_list).width()>a(vars.thumb_tray).width()){a(vars.thumb_back+","+vars.thumb_forward).fadeIn("fast");a(vars.thumb_list).stop().animate({left:0},200)}else{a(vars.thumb_back+","+vars.thumb_forward).fadeOut("fast")}}})},goTo:function(b){if(api.options.progress_bar&&!vars.is_paused){a(vars.progress_bar).stop().animate({left:-a(window).width()},0);theme.progressBar()}},playToggle:function(b){if(b=="play"){if(a(vars.play_button).attr("src")){a(vars.play_button).attr("src",vars.image_path+"pause.png")}if(api.options.progress_bar&&!vars.is_paused){theme.progressBar()}}else{if(b=="pause"){if(a(vars.play_button).attr("src")){a(vars.play_button).attr("src",vars.image_path+"play.png")}if(api.options.progress_bar&&vars.is_paused){a(vars.progress_bar).stop().animate({left:-a(window).width()},0)}}}},beforeAnimation:function(b){if(api.options.progress_bar&&!vars.is_paused){a(vars.progress_bar).stop().animate({left:-a(window).width()},0)}if(a(vars.slide_caption).length){(api.getField("title"))?a(vars.slide_caption).html(api.getField("title")):a(vars.slide_caption).html("")}if(vars.slide_current.length){a(vars.slide_current).html(vars.current_slide+1)}if(api.options.thumb_links){a(".current-thumb").removeClass("current-thumb");a("li",vars.thumb_list).eq(vars.current_slide).addClass("current-thumb");if(a(vars.thumb_list).width()>a(vars.thumb_tray).width()){if(b=="next"){if(vars.current_slide==0){vars.thumb_page=0;a(vars.thumb_list).stop().animate({left:vars.thumb_page},{duration:500,easing:"easeOutExpo"})}else{if(a(".current-thumb").offset().left-a(vars.thumb_tray).offset().left>=vars.thumb_interval){vars.thumb_page=vars.thumb_page-vars.thumb_interval;a(vars.thumb_list).stop().animate({left:vars.thumb_page},{duration:500,easing:"easeOutExpo"})}}}else{if(b=="prev"){if(vars.current_slide==api.options.slides.length-1){vars.thumb_page=Math.floor(a(vars.thumb_list).width()/vars.thumb_interval)*-vars.thumb_interval;if(a(vars.thumb_list).width()<=-vars.thumb_page){vars.thumb_page=vars.thumb_page+vars.thumb_interval}a(vars.thumb_list).stop().animate({left:vars.thumb_page},{duration:500,easing:"easeOutExpo"})}else{if(a(".current-thumb").offset().left-a(vars.thumb_tray).offset().left<0){if(vars.thumb_page+vars.thumb_interval>0){return false}vars.thumb_page=vars.thumb_page+vars.thumb_interval;a(vars.thumb_list).stop().animate({left:vars.thumb_page},{duration:500,easing:"easeOutExpo"})}}}}}}},afterAnimation:function(){if(api.options.progress_bar&&!vars.is_paused){theme.progressBar()}},progressBar:function(){a(vars.progress_bar).stop().animate({left:-a(window).width()},0).animate({left:0},api.options.slide_interval)}};a.supersized.themeVars={progress_delay:false,thumb_page:false,thumb_interval:false,image_path:OC.webroot+"/apps/gallery/img/supersized/",play_button:"#pauseplay",next_slide:"#nextslide",prev_slide:"#prevslide",next_thumb:"#nextthumb",prev_thumb:"#prevthumb",slide_caption:"#slidecaption",slide_current:".slidenumber",slide_total:".totalslides",slide_list:"#slide-list",thumb_tray:"#thumb-tray",thumb_list:"#thumb-list",thumb_forward:"#thumb-forward",thumb_back:"#thumb-back",tray_arrow:"#tray-arrow",tray_button:"#tray-button",progress_bar:"#progress-bar"};a.supersized.themeOptions={progress_bar:1,mouse_scrub:0}})(jQuery); diff --git a/apps/gallery/l10n/ca.php b/apps/gallery/l10n/ca.php index 165414fba2..1c5848cee2 100644 --- a/apps/gallery/l10n/ca.php +++ b/apps/gallery/l10n/ca.php @@ -1,9 +1,9 @@ "Fotos", -"Settings" => "Arranjament", -"Rescan" => "Escaneja de nou", -"Stop" => "Atura", -"Share" => "Comparteix", +"Share gallery" => "Comperteix la galeria", +"Error: " => "Error: ", +"Internal error" => "Error intern", +"Slideshow" => "Passi de diapositives", "Back" => "Enrera", "Remove confirmation" => "Elimina la confirmació", "Do you want to remove album" => "Voleu eliminar l'àlbum", diff --git a/apps/gallery/l10n/cs_CZ.php b/apps/gallery/l10n/cs_CZ.php index d008e9d28b..02d1c02d6e 100644 --- a/apps/gallery/l10n/cs_CZ.php +++ b/apps/gallery/l10n/cs_CZ.php @@ -1,12 +1,7 @@ "Obrázky", -"Settings" => "Nastavení", -"Rescan" => "Znovu prohledat", -"Stop" => "Zastavit", -"Share" => "Sdílet", -"Back" => "Zpět", -"Remove confirmation" => "Potvrzení odebrání", -"Do you want to remove album" => "Chcete odstranit album?", -"Change album name" => "Změnit název alba", -"New album name" => "Název nového alba" +"Share gallery" => "Sdílet galerii", +"Error: " => "Chyba: ", +"Internal error" => "Vnitřní chyba", +"Slideshow" => "Přehrávání" ); diff --git a/apps/gallery/l10n/de.php b/apps/gallery/l10n/de.php index 6c3d9fc738..cd580cf303 100644 --- a/apps/gallery/l10n/de.php +++ b/apps/gallery/l10n/de.php @@ -1,9 +1,9 @@ "Bilder", -"Settings" => "Einstellungen", -"Rescan" => "Erneut Scannen", -"Stop" => "Stopp", -"Share" => "Teilen", +"Share gallery" => "Galerie teilen", +"Error: " => "Fehler:", +"Internal error" => "Interner Fehler", +"Slideshow" => "Slideshow", "Back" => "Zurück", "Remove confirmation" => "Bestätigung entfernen", "Do you want to remove album" => "Soll das Album entfernt werden", diff --git a/apps/gallery/l10n/el.php b/apps/gallery/l10n/el.php index 3983011a0c..47bc3af2bb 100644 --- a/apps/gallery/l10n/el.php +++ b/apps/gallery/l10n/el.php @@ -1,9 +1,9 @@ "Εικόνες", -"Settings" => "Ρυθμίσεις", -"Rescan" => "Επανασάρωση", -"Stop" => "Διακοπή", -"Share" => "Κοινοποίηση", +"Share gallery" => "Κοινοποίηση συλλογής", +"Error: " => "Σφάλμα: ", +"Internal error" => "Εσωτερικό σφάλμα", +"Slideshow" => "Προβολή Διαφανειών", "Back" => "Επιστροφή", "Remove confirmation" => "Αφαίρεση επιβεβαίωσης", "Do you want to remove album" => "Θέλετε να αφαιρέσετε το άλμπουμ", diff --git a/apps/gallery/l10n/es.php b/apps/gallery/l10n/es.php index 03e8d6a456..aa425a0bd0 100644 --- a/apps/gallery/l10n/es.php +++ b/apps/gallery/l10n/es.php @@ -1,9 +1,9 @@ "Imágenes", -"Settings" => "Preferencias", -"Rescan" => "Refrescar", -"Stop" => "Parar", -"Share" => "Compartir", +"Share gallery" => "Compartir galería", +"Error: " => "Fallo ", +"Internal error" => "Fallo interno", +"Slideshow" => "Presentación", "Back" => "Atrás", "Remove confirmation" => "Borrar confirmación", "Do you want to remove album" => "¿Quieres eliminar el álbum", diff --git a/apps/gallery/l10n/fi_FI.php b/apps/gallery/l10n/fi_FI.php index 267bb5e547..659289ae41 100644 --- a/apps/gallery/l10n/fi_FI.php +++ b/apps/gallery/l10n/fi_FI.php @@ -1,9 +1,9 @@ "Kuvat", -"Settings" => "Asetukset", -"Rescan" => "Etsi uusia", -"Stop" => "Pysäytä", -"Share" => "Jaa", +"Share gallery" => "Jaa galleria", +"Error: " => "Virhe: ", +"Internal error" => "Sisäinen virhe", +"Slideshow" => "Diaesitys", "Back" => "Takaisin", "Remove confirmation" => "Poiston vahvistus", "Do you want to remove album" => "Tahdotko poistaa albumin", diff --git a/apps/gallery/l10n/fr.php b/apps/gallery/l10n/fr.php index dfd668ebe8..04421236e1 100644 --- a/apps/gallery/l10n/fr.php +++ b/apps/gallery/l10n/fr.php @@ -1,9 +1,9 @@ "Images", -"Settings" => "Préférences", -"Rescan" => "Analyser à nouveau", -"Stop" => "Arrêter", -"Share" => "Partager", +"Share gallery" => "Partager la galerie", +"Error: " => "Erreur :", +"Internal error" => "Erreur interne", +"Slideshow" => "Diaporama", "Back" => "Retour", "Remove confirmation" => "Enlever la confirmation", "Do you want to remove album" => "Voulez-vous supprimer l'album", diff --git a/apps/gallery/l10n/it.php b/apps/gallery/l10n/it.php index e21a1d6524..ef8d596e7e 100644 --- a/apps/gallery/l10n/it.php +++ b/apps/gallery/l10n/it.php @@ -1,9 +1,9 @@ "Immagini", -"Settings" => "Impostazioni", -"Rescan" => "Nuova scansione", -"Stop" => "Ferma", -"Share" => "Condividi", +"Share gallery" => "Condividi la galleria", +"Error: " => "Errore: ", +"Internal error" => "Errore interno", +"Slideshow" => "Presentazione", "Back" => "Indietro", "Remove confirmation" => "Rimuovi conferma", "Do you want to remove album" => "Vuoi rimuovere l'album", diff --git a/apps/gallery/l10n/pl.php b/apps/gallery/l10n/pl.php index 1ff636ac2a..8c0bd0cb98 100644 --- a/apps/gallery/l10n/pl.php +++ b/apps/gallery/l10n/pl.php @@ -1,9 +1,9 @@ "Zdjęcia", -"Settings" => "Ustawienia", -"Rescan" => "Przeszukaj", -"Stop" => "Stop", -"Share" => "Współdziel", +"Share gallery" => "Udostępnij galerię", +"Error: " => "Błąd: ", +"Internal error" => "Błąd wewnętrzny", +"Slideshow" => "Pokaz slajdów", "Back" => "Wróć", "Remove confirmation" => "Usuń potwierdzenie", "Do you want to remove album" => "Czy chcesz usunąć album", diff --git a/apps/gallery/l10n/pt_PT.php b/apps/gallery/l10n/pt_PT.php index 54e99cf245..8b7aa2a23b 100644 --- a/apps/gallery/l10n/pt_PT.php +++ b/apps/gallery/l10n/pt_PT.php @@ -1,12 +1,7 @@ "Imagens", -"Settings" => "Definições", -"Rescan" => "Atualizar", -"Stop" => "Parar", -"Share" => "Partilhar", -"Back" => "Voltar", -"Remove confirmation" => "Remove confirmação", -"Do you want to remove album" => "Deseja remover o album", -"Change album name" => "Mudar o nome do album", -"New album name" => "Novo nome do album" +"Share gallery" => "Partilhar a galeria", +"Error: " => "Erro: ", +"Internal error" => "Erro interno", +"Slideshow" => "Slideshow" ); diff --git a/apps/gallery/l10n/ru.php b/apps/gallery/l10n/ru.php index f1c530ed28..d6e33e4b01 100644 --- a/apps/gallery/l10n/ru.php +++ b/apps/gallery/l10n/ru.php @@ -1,12 +1,7 @@ "Рисунки", -"Settings" => "Настройки", -"Rescan" => "Обновить", -"Stop" => "Остановить", -"Share" => "Поделиться", -"Back" => "Назад", -"Remove confirmation" => "Подтверждение удаления", -"Do you want to remove album" => "Вы хотите удалить альбом?", -"Change album name" => "Изменить имя альбома", -"New album name" => "Новое имя альбома" +"Share gallery" => "Опубликовать", +"Error: " => "Ошибка", +"Internal error" => "Внутренняя ошибка", +"Slideshow" => "Слайдшоу" ); diff --git a/apps/gallery/l10n/sl.php b/apps/gallery/l10n/sl.php index 5e06123986..8d3bb9f588 100644 --- a/apps/gallery/l10n/sl.php +++ b/apps/gallery/l10n/sl.php @@ -1,9 +1,9 @@ "Slike", -"Settings" => "Nastavitve", -"Rescan" => "Ponovno preišči", -"Stop" => "Stop", -"Share" => "Deli", +"Share gallery" => "Daj galerijo v souporabo", +"Error: " => "Napaka: ", +"Internal error" => "Notranja napaka", +"Slideshow" => "predstavitev", "Back" => "Nazaj", "Remove confirmation" => "Odstrani potrditev", "Do you want to remove album" => "Ali želite odstraniti album", diff --git a/apps/gallery/l10n/sv.php b/apps/gallery/l10n/sv.php index 520d271df1..b63a89d90f 100644 --- a/apps/gallery/l10n/sv.php +++ b/apps/gallery/l10n/sv.php @@ -1,9 +1,9 @@ "Bilder", -"Settings" => "Inställningar", -"Rescan" => "Sök igen", -"Stop" => "Stoppa", -"Share" => "Dela", +"Share gallery" => "Dela galleri", +"Error: " => "Fel:", +"Internal error" => "Internt fel", +"Slideshow" => "Bildspel", "Back" => "Tillbaka", "Remove confirmation" => "Vill du säkert ta bort", "Do you want to remove album" => "Vill du ta bort albumet", diff --git a/apps/gallery/l10n/th_TH.php b/apps/gallery/l10n/th_TH.php index 34da8dc57f..9bb699e264 100644 --- a/apps/gallery/l10n/th_TH.php +++ b/apps/gallery/l10n/th_TH.php @@ -1,12 +1,7 @@ "รูปภาพ", -"Settings" => "ตั้งค่า", -"Rescan" => "ตรวจสอบอีกครั้ง", -"Stop" => "หยุด", -"Share" => "แชร์", -"Back" => "ย้อนกลับ", -"Remove confirmation" => "การยืนยันการลบ", -"Do you want to remove album" => "คุณต้องการลบอัลบั้มออกหรือไม่", -"Change album name" => "เปลี่ยนชื่ออัลบั้ม", -"New album name" => "ชื่อใหม่ของอัลบั้ม" +"Share gallery" => "แชร์ข้อมูลแกลอรี่", +"Error: " => "พบข้อผิดพลาด: ", +"Internal error" => "เกิดข้อผิดพลาดภายในระบบ", +"Slideshow" => "ภาพสไลด์โชว์" ); diff --git a/apps/gallery/l10n/tr.php b/apps/gallery/l10n/tr.php index c42592448c..7d007fa66e 100644 --- a/apps/gallery/l10n/tr.php +++ b/apps/gallery/l10n/tr.php @@ -1,9 +1,9 @@ "Resimler", -"Settings" => "Ayarlar", -"Rescan" => "Yeniden Tara ", -"Stop" => "Durdur", -"Share" => "Paylaş", +"Share gallery" => "Galeriyi paylaş", +"Error: " => "Hata: ", +"Internal error" => "İç hata", +"Slideshow" => "Slide Gösterim", "Back" => "Geri", "Remove confirmation" => "Doğrulamayı kaldır", "Do you want to remove album" => "Albümü silmek istiyor musunuz", diff --git a/apps/gallery/l10n/vi.php b/apps/gallery/l10n/vi.php new file mode 100644 index 0000000000..d1d7fc64fc --- /dev/null +++ b/apps/gallery/l10n/vi.php @@ -0,0 +1,11 @@ + "Hình ảnh", +"Share gallery" => "Chia sẻ gallery", +"Error: " => "Lỗi :", +"Internal error" => "Lỗi nội bộ", +"Back" => "Trở lại", +"Remove confirmation" => "Xóa xác nhận", +"Do you want to remove album" => "Bạn muốn xóa album này ", +"Change album name" => "Đổi tên album", +"New album name" => "Tên album mới" +); diff --git a/apps/gallery/l10n/zh_CN.php b/apps/gallery/l10n/zh_CN.php index ffa321f7de..6aa4fc7f35 100644 --- a/apps/gallery/l10n/zh_CN.php +++ b/apps/gallery/l10n/zh_CN.php @@ -1,12 +1,7 @@ "图片", -"Settings" => "设置", -"Rescan" => "重新扫描", -"Stop" => "停止", -"Share" => "分享", -"Back" => "返回", -"Remove confirmation" => "移除确认", -"Do you want to remove album" => "您是否想要移除相册", -"Change album name" => "修改相册名称", -"New album name" => "新相册名称" +"Share gallery" => "分享图库", +"Error: " => "错误:", +"Internal error" => "内部错误", +"Slideshow" => "幻灯片" ); diff --git a/apps/gallery/lib/album.php b/apps/gallery/lib/album.php index b9aa535629..d1b29a5992 100644 --- a/apps/gallery/lib/album.php +++ b/apps/gallery/lib/album.php @@ -77,7 +77,7 @@ class OC_Gallery_Album { $sql .= ' AND `parent_path` = ?'; $args[] = $parent; } - $order = OCP\Config::getUserValue($owner, 'gallery', 'order', 'ASC'); + $order = OCP\Config::getUserValue($owner, 'gallery', 'order', 'ASC'); $sql .= ' ORDER BY `album_name` ' . $order; $stmt = OCP\DB::prepare($sql); @@ -90,25 +90,22 @@ class OC_Gallery_Album { } public static function changeThumbnailPath($oldname, $newname) { - - $thumbpath = OC::$CONFIG_DATADIRECTORY.'/../gallery/'; - rename($thumbpath.$oldname.'.png', $thumbpath.$newname.'.png'); + $view = OCP\Files::getStorage('gallery'); + $view->rename($oldname.'.png', $newname.'.png'); } public static function getAlbumSize($id){ $sql = 'SELECT COUNT(*) AS `size` FROM `*PREFIX*gallery_photos` WHERE `album_id` = ?'; - $stmt = OCP\DB::prepare($sql); - $result=$stmt->execute(array($id))->fetchRow(); - return $result['size']; + $stmt = OCP\DB::prepare($sql); + $result=$stmt->execute(array($id))->fetchRow(); + return $result['size']; } - public static function getIntermediateGallerySize($path) { - $path .= '%'; + public static function getIntermediateGallerySize($path) { + $path .= '%'; $sql = 'SELECT COUNT(*) AS `size` FROM `*PREFIX*gallery_photos` AS `photos`, `*PREFIX*gallery_albums` AS `albums` WHERE `photos`.`album_id` = `albums`.`album_id` AND `uid_owner` = ? AND `file_path` LIKE ?'; - $stmt = OCP\DB::prepare($sql); - $result = $stmt->execute(array(OCP\USER::getUser(), $path))->fetchRow(); - return $result['size']; - } + $stmt = OCP\DB::prepare($sql); + $result = $stmt->execute(array(OCP\USER::getUser(), $path))->fetchRow(); + return $result['size']; + } } - -?> diff --git a/apps/gallery/lib/hooks_handlers.php b/apps/gallery/lib/hooks_handlers.php index a9f4dc6aff..3bafdb5cf4 100644 --- a/apps/gallery/lib/hooks_handlers.php +++ b/apps/gallery/lib/hooks_handlers.php @@ -21,7 +21,7 @@ * */ -OCP\Util::connectHook(OC_Filesystem::CLASSNAME, OC_Filesystem::signal_delete, "OC_Gallery_Hooks_Handlers", "removePhoto"); +OCP\Util::connectHook('OC_Filesystem', 'delete', "OC_Gallery_Hooks_Handlers", "removePhoto"); //OCP\Util::connectHook(OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_rename, "OC_Gallery_Hooks_Handlers", "renamePhoto"); require_once(OC::$CLASSPATH['Pictures_Managers']); @@ -38,5 +38,3 @@ class OC_Gallery_Hooks_Handlers { //TODO: implement this } } - -?> diff --git a/apps/gallery/lib/images_utils.php b/apps/gallery/lib/images_utils.php index ac3a383c97..f5e37cf1de 100644 --- a/apps/gallery/lib/images_utils.php +++ b/apps/gallery/lib/images_utils.php @@ -60,5 +60,3 @@ function CroppedThumbnail($imgSrc,$thumbnail_width,$thumbnail_height, $tgtImg, $ imagedestroy($process); imagedestroy($myImage); } - -?> diff --git a/apps/gallery/lib/managers.php b/apps/gallery/lib/managers.php index 82356e54dd..666c6d6893 100644 --- a/apps/gallery/lib/managers.php +++ b/apps/gallery/lib/managers.php @@ -2,10 +2,9 @@ namespace OC\Pictures; -require_once('lib/base.php'); - -class DatabaseManager { +class DatabaseManager { private static $instance = null; + protected $cache = array(); const TAG = 'DatabaseManager'; public static function getInstance() { @@ -14,22 +13,44 @@ class DatabaseManager { return self::$instance; } + protected function getPathData($path) { + $stmt = \OCP\DB::prepare('SELECT * FROM `*PREFIX*pictures_images_cache` + WHERE `uid_owner` LIKE ? AND `path` LIKE ? AND `path` NOT LIKE ?'); + $path_match = $path.'/%'; + $path_notmatch = $path.'/%/%'; + $result = $stmt->execute(array(\OCP\USER::getUser(), $path_match, $path_notmatch)); + $this->cache[$path] = array(); + while (($row = $result->fetchRow()) != false) { + $this->cache[$path][$row['path']] = $row; + } + } + + public function setFileData($path, $width, $height) { + $stmt = \OCP\DB::prepare('INSERT INTO `*PREFIX*pictures_images_cache` (`uid_owner`, `path`, `width`, `height`) VALUES (?, ?, ?, ?)'); + $stmt->execute(array(\OCP\USER::getUser(), $path, $width, $height)); + $ret = array('path' => $path, 'width' => $width, 'height' => $height); + $dir = dirname($path); + $this->cache[$dir][$path] = $ret; + return $ret; + } + public function getFileData($path) { $gallery_path = \OCP\Config::getSystemValue( 'datadirectory' ).'/'.\OC_User::getUser().'/gallery'; $path = $gallery_path.$path; - $stmt = \OCP\DB::prepare('SELECT * FROM `*PREFIX*pictures_images_cache` WHERE `uid_owner` LIKE ? AND `path` = ?'); - $result = $stmt->execute(array(\OCP\USER::getUser(), $path)); - if (($row = $result->fetchRow()) != false) { - return $row; + $dir = dirname($path); + if (!isset($this->cache[$dir])) { + $this->getPathData($dir); + } + if (isset($this->cache[$dir][$path])) { + return $this->cache[$dir][$path]; } $image = new \OC_Image(); if (!$image->loadFromFile($path)) { return false; } - $stmt = \OCP\DB::prepare('INSERT INTO `*PREFIX*pictures_images_cache` (`uid_owner`, `path`, `width`, `height`) VALUES (?, ?, ?, ?)'); - $stmt->execute(array(\OCP\USER::getUser(), $path, $image->width(), $image->height())); - $ret = array('path' => $path, 'width' => $image->width(), 'height' => $image->height()); + $ret = $this->setFileData($path, $image->width(), $image->height()); unset($image); + $this->cache[$dir][$path] = $ret; return $ret; } @@ -40,6 +61,7 @@ class ThumbnailsManager { private static $instance = null; const TAG = 'ThumbnailManager'; + const THUMBNAIL_HEIGHT = 150; public static function getInstance() { if (self::$instance === null) @@ -48,9 +70,9 @@ class ThumbnailsManager { } public function getThumbnail($path) { - $gallery_path = \OCP\Config::getSystemValue( 'datadirectory' ).'/'.\OC_User::getUser().'/gallery'; - if (file_exists($gallery_path.$path)) { - return new \OC_Image($gallery_path.$path); + $gallery_storage = \OCP\Files::getStorage('gallery'); + if ($gallery_storage->file_exists($path)) { + return new \OC_Image($gallery_storage->getLocalFile($path)); } if (!\OC_Filesystem::file_exists($path)) { \OC_Log::write(self::TAG, 'File '.$path.' don\'t exists', \OC_Log::WARN); @@ -59,27 +81,39 @@ class ThumbnailsManager { $image = new \OC_Image(); $image->loadFromFile(\OC_Filesystem::getLocalFile($path)); if (!$image->valid()) return false; - + $image->fixOrientation(); - - $ret = $image->preciseResize(floor((150*$image->width())/$image->height()), 150); + + $ret = $image->preciseResize( floor((self::THUMBNAIL_HEIGHT*$image->width())/$image->height()), self::THUMBNAIL_HEIGHT ); if (!$ret) { \OC_Log::write(self::TAG, 'Couldn\'t resize image', \OC_Log::ERROR); unset($image); return false; } - - $image->save($gallery_path.'/'.$path); + $l = $gallery_storage->getLocalFile($path); + + $image->save($l); return $image; } - + + public function getThumbnailWidth($image) { + return floor((self::THUMBNAIL_HEIGHT*$image->widthTopLeft())/$image->heightTopLeft()); + } + public function getThumbnailInfo($path) { $arr = DatabaseManager::getInstance()->getFileData($path); if (!$arr) { - $thubnail = $this->getThumbnail($path); - unset($thubnail); - $arr = DatabaseManager::getInstance()->getFileData($path); + if (!\OC_Filesystem::file_exists($path)) { + \OC_Log::write(self::TAG, 'File '.$path.' don\'t exists', \OC_Log::WARN); + return false; + } + $image = new \OC_Image(); + $image->loadFromFile(\OC_Filesystem::getLocalFile($path)); + if (!$image->valid()) { + return false; + } + $arr = DatabaseManager::getInstance()->setFileData($path, $this->getThumbnailWidth($image), self::THUMBNAIL_HEIGHT); } $ret = array('filepath' => $arr['path'], 'width' => $arr['width'], @@ -88,13 +122,12 @@ class ThumbnailsManager { } public function delete($path) { - $thumbnail = \OCP\Config::getSystemValue('datadirectory').'/'.\OC_User::getUser()."/gallery".$path; - if (file_exists($thumbnail)) { - unlink($thumbnail); + $thumbnail_storage = \OCP\Files::getStorage('gallery'); + if ($thumbnail_storage->file_exists($path)) { + $thumbnail_storage->unlink($path); } } private function __construct() {} } -?> diff --git a/apps/gallery/lib/photo.php b/apps/gallery/lib/photo.php index 1b4f908773..f073ac9cbb 100644 --- a/apps/gallery/lib/photo.php +++ b/apps/gallery/lib/photo.php @@ -1,26 +1,25 @@ . -* -*/ - + * ownCloud - gallery application + * + * @author Bartek Przybylski + * @copyright 2012 Bartek Przybylski bart.p.pl@gmail.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + */ class OC_Gallery_Photo { public static function create($albumId, $img){ $stmt = OCP\DB::prepare('INSERT INTO `*PREFIX*gallery_photos` (`album_id`, `file_path`) VALUES (?, ?)'); @@ -36,62 +35,95 @@ class OC_Gallery_Photo { $stmt = OCP\DB::prepare($sql); return $stmt->execute($args); } - public static function findForAlbum($owner, $album_name){ + + public static function findForAlbum($owner, $album_name) { $stmt = OCP\DB::prepare('SELECT *' - .' FROM `*PREFIX*gallery_photos photos`,' - .' `*PREFIX*gallery_albums albums`' - .' WHERE `albums`.`uid_owner` = ?' - .' AND `albums`.`album_name` = ?' - .' AND `photos`.`album_id` = `albums`.`album_id`'); + .' FROM `*PREFIX*gallery_photos photos`,' + .' `*PREFIX*gallery_albums albums`' + .' WHERE `albums`.`uid_owner` = ?' + .' AND `albums`.`album_name` = ?' + .' AND `photos`.`album_id` = `albums`.`album_id`'); return $stmt->execute(array($owner, $album_name)); } - public static function removeByPath($path, $album_id) { - $stmt = OCP\DB::prepare('DELETE FROM `*PREFIX*gallery_photos` WHERE `file_path` LIKE ? AND `album_id` = ?'); + public static function removeByPath($path, $album_id) { + $stmt = OCP\DB::prepare('DELETE FROM `*PREFIX*gallery_photos` WHERE `file_path` LIKE ? AND `album_id` = ?'); $stmt->execute(array($path, $album_id)); } public static function removeById($id) { - $stmt = OCP\DB::prepare('DELETE FROM `*PREFIX*gallery_photos` WHERE `photo_id` = ?'); + $stmt = OCP\DB::prepare('DELETE FROM *PREFIX*gallery_photos WHERE photo_id = ?'); $stmt->execute(array($id)); } public static function removeByAlbumId($albumid) { - $stmt = OCP\DB::prepare('DELETE FROM `*PREFIX*gallery_photos` WHERE `album_id` = ?'); + $stmt = OCP\DB::prepare('DELETE FROM *PREFIX*gallery_photos WHERE album_id = ?'); $stmt->execute(array($albumid)); } public static function changePath($oldAlbumId, $newAlbumId, $oldpath, $newpath) { - $stmt = OCP\DB::prepare("UPDATE `*PREFIX*gallery_photos` SET `file_path` = ?, `album_id` = ? WHERE `album_id` = ? AND `file_path` = ?"); + $stmt = OCP\DB::prepare("UPDATE *PREFIX*gallery_photos SET file_path = ?, album_id = ? WHERE album_id = ? and file_path = ?"); $stmt->execute(array($newpath, $newAlbumId, $oldAlbumId, $oldpath)); } public static function getThumbnail($image_name, $owner = null) { - if (!$owner) $owner = OCP\USER::getUser(); - $save_dir = OCP\Config::getSystemValue("datadirectory").'/'. $owner .'/gallery/'; - $save_dir .= dirname($image_name). '/'; - $image_path = $image_name; - $thumb_file = $save_dir . basename($image_name); - if (!is_dir($save_dir)) { - mkdir($save_dir, 0777, true); + if (!$owner) + $owner = OCP\USER::getUser(); + $view = OCP\Files::getStorage('gallery'); + $save_dir = dirname($image_name); + if (!$view->is_dir($save_dir)) { + $view->mkdir($save_dir); } - if (file_exists($thumb_file)) { - $image = new OC_Image($thumb_file); + $view->chroot($view->getRoot() . '/' . $save_dir); + $thumb_file = basename($image_name); + if ($view->file_exists($thumb_file)) { + $image = new OC_Image($view->fopen($thumb_file, 'r')); } else { - $image_path = OC_Filesystem::getLocalFile($image_path); - if(!file_exists($image_path)) { + $image_path = OC_Filesystem::getLocalFile($image_name); + if (!file_exists($image_path)) { return null; } $image = new OC_Image($image_path); if ($image->valid()) { $image->centerCrop(200); $image->fixOrientation(); - $image->save($thumb_file); + $image->save($view->getLocalFile($thumb_file)); } } if ($image->valid()) { return $image; - }else{ + } else { + $image->destroy(); + } + return null; + } + + public static function getViewImage($image_name, $owner = null) { + if (!$owner) $owner = OCP\USER::getUser(); + $save_dir = OCP\Config::getSystemValue("datadirectory") . '/' . $owner . '/gallery'; + $save_dir .= dirname($image_name) . '/view/'; + $image_path = $image_name; + $view_file = $save_dir . basename($image_name); + if (!is_dir($save_dir)) { + mkdir($save_dir, 0777, true); + } + if (file_exists($view_file)) { + $image = new OC_Image($view_file); + } else { + $image_path = OC_Filesystem::getLocalFile($image_path); + if (!file_exists($image_path)) { + return null; + } + $image = new OC_Image($image_path); + if ($image->valid()) { + $image->resize(1200); + $image->fixOrientation(); + $image->save($view_file); + } + } + if ($image->valid()) { + return $image; + } else { $image->destroy(); } return null; @@ -100,4 +132,5 @@ class OC_Gallery_Photo { public static function getGalleryRoot() { return OCP\Config::getUserValue(OCP\USER::getUser(), 'gallery', 'root', ''); } + } diff --git a/apps/gallery/lib/scanner.php b/apps/gallery/lib/scanner.php index b6c402022b..79b8ad4923 100644 --- a/apps/gallery/lib/scanner.php +++ b/apps/gallery/lib/scanner.php @@ -81,7 +81,8 @@ class OC_Gallery_Scanner { $image->destroy(); } } - imagepng($thumbnail, OCP\Config::getSystemValue("datadirectory").'/'. OCP\USER::getUser() .'/gallery/' . $albumName.'.png'); + $view = OCP\Files::getStorage('gallery'); + imagepng($thumbnail, $view->getLocalFile($albumName.'.png')); imagedestroy($thumbnail); } diff --git a/apps/gallery/lib/share.php b/apps/gallery/lib/share.php new file mode 100644 index 0000000000..d6c5f40d49 --- /dev/null +++ b/apps/gallery/lib/share.php @@ -0,0 +1,42 @@ +. +*/ + +abstract class OC_Share_Photo_Backend implements OCP\Share_Backend { + + public $dependsOn = 'file'; + public $supportedFileExtensions = array('jpg', 'png', 'gif'); + + public function getSource($item, $uid) { + return array('item' => 'blah.jpg', 'file' => $item); + } + + public function generateTarget($item, $uid, $exclude = null) { + // TODO Make sure target path doesn't exist already + return $item; + } + + public function formatItems($items, $format, $parameters = null) { + + } + +} + +?> \ No newline at end of file diff --git a/apps/gallery/lib/tiles.php b/apps/gallery/lib/tiles.php index 53ea97ff05..e36d26d319 100644 --- a/apps/gallery/lib/tiles.php +++ b/apps/gallery/lib/tiles.php @@ -33,7 +33,7 @@ class TilesLine { } public function setAvailableSpace($space) { - $available_space = $space; + $this->available_space = $space; } public function getTilesCount() { @@ -95,7 +95,7 @@ class TileSingle extends TileBase { public function get($extra = '') { // !HACK! file path needs to be encoded twice because files app decode twice url, so any special chars like + or & in filename // !HACK! will result in failing of opening them - return ''; + return ''; } public function getMiniatureSrc() { @@ -168,11 +168,9 @@ class TileStack extends TileBase { } public function getOnClickAction() { - return 'javascript:openNewGal(\''.htmlentities($this->stack_name).'\');'; + return 'javascript:openNewGal(\''.rawurlencode($this->stack_name).'\');'; } private $tiles_array; private $stack_name; } - -?> diff --git a/apps/gallery/lib/tiles_test.php b/apps/gallery/lib/tiles_test.php index 022a88f75c..02d567c628 100644 --- a/apps/gallery/lib/tiles_test.php +++ b/apps/gallery/lib/tiles_test.php @@ -83,5 +83,3 @@ if ($ts->getCount() != 0) { } echo $tl->get(); - -?> diff --git a/apps/gallery/templates/index.php b/apps/gallery/templates/index.php index a41bf3c47b..b2efd5342f 100644 --- a/apps/gallery/templates/index.php +++ b/apps/gallery/templates/index.php @@ -1,52 +1,6 @@ - - '; + echo ''; } } -?>
          +?> +
          + +
          these are taken for the stack preview - -for($i = 0; $i < count($images); $i++) { - $prev_dir_arr = explode('/', $previous_element); - $dir_arr = explode('/', $images[$i]); - - if(count($dir_arr) == 1) { // getting the images in this directory - $root_images[] = $root.$images[$i]; - } else { - if(strcmp($prev_dir_arr[0], $dir_arr[0]) != 0) { // if we entered a new directory - if(count($second_level_images) == 0) { // if we don't have images in this directory - if(count($fallback_images) != 0) { // but have fallback_images - $tl->addTile(new \OC\Pictures\TileStack($fallback_images, $prev_dir_arr[0])); - $fallback_images = array(); - } - } else { // if we collected images for this directory - $tl->addTile(new \OC\Pictures\TileStack($second_level_images, $prev_dir_arr[0])); - $fallback_images = array(); - $second_level_images = array(); - } - } - if (count($dir_arr) == 2) { // These are the pics in our current subdir - $second_level_images[] = $root.$images[$i]; - $fallback_images = array(); - } else { // These are images from the deeper directories - if(count($second_level_images) == 0) { - $fallback_images[] = $root.$images[$i]; - } - } - // have us a little something to compare against - $previous_element = $images[$i]; - } -} - -// if last element in the directory was a directory we don't want to miss it :) -if(count($second_level_images)>0) { - $tl->addTile(new \OC\Pictures\TileStack($second_level_images, $prev_dir_arr[0])); -} - -// if last element in the directory was a directory with no second_level_images we also don't want to miss it ... -if(count($fallback_images)>0) { - $tl->addTile(new \OC\Pictures\TileStack($fallback_images, $prev_dir_arr[0])); -} - -// and finally our images actually stored in the root folder -for($i = 0; $iaddTile(new \OC\Pictures\TileSingle($root_images[$i])); -} - -echo $tl->get(); +echo $_['tl']->get(); ?>
          + + + diff --git a/apps/gallery/templates/view_album.php b/apps/gallery/templates/view_album.php deleted file mode 100644 index c16ed69c06..0000000000 --- a/apps/gallery/templates/view_album.php +++ /dev/null @@ -1,47 +0,0 @@ - - - -
          - -
          -
          - - - - - - - - diff --git a/apps/impress/appinfo/app.php b/apps/impress/appinfo/app.php new file mode 100644 index 0000000000..82f098a030 --- /dev/null +++ b/apps/impress/appinfo/app.php @@ -0,0 +1,38 @@ +. + * + */ + +OCP\Util::addStyle( 'impress', 'style'); + +OCP\App::register(array('order' => 70, 'id' => 'impress', 'name' => 'Impress')); + +OCP\App::addNavigationEntry(array( + 'id' => 'impress_index', + 'order' => 80, + 'href' => OCP\Util::linkTo('impress', 'index.php'), + 'icon' => OCP\Util::imagePath('impress', 'impress.png'), + 'name' => 'Impress') +); + + + +?> diff --git a/apps/impress/appinfo/info.xml b/apps/impress/appinfo/info.xml new file mode 100644 index 0000000000..0be9c170f6 --- /dev/null +++ b/apps/impress/appinfo/info.xml @@ -0,0 +1,10 @@ + + + impress + Impress + A Player for impress presentations. Many thanks to Bartek Szopka for creating the wonderful impress.js library who made this app possible. + AGPL + Frank Karlitschek + 4 + true + diff --git a/apps/impress/appinfo/version b/apps/impress/appinfo/version new file mode 100644 index 0000000000..9f8e9b69a3 --- /dev/null +++ b/apps/impress/appinfo/version @@ -0,0 +1 @@ +1.0 \ No newline at end of file diff --git a/apps/impress/css/player.css b/apps/impress/css/player.css new file mode 100644 index 0000000000..0ed7130fd0 --- /dev/null +++ b/apps/impress/css/player.css @@ -0,0 +1,703 @@ +/* + So you like the style of impress.js demo? + Or maybe you are just curious how it was done? + + You couldn't find a better place to find out! + + Welcome to the stylesheet impress.js demo presentation. + + Please remember that it is not meant to be a part of impress.js and is + not required by impress.js. + I expect that anyone creating a presentation for impress.js would create + their own set of styles. + + But feel free to read through it and learn how to get the most of what + impress.js provides. + + And let me be your guide. + + Shall we begin? +*/ + + +/* + We start with a good ol' reset. + That's the one by Eric Meyer http://meyerweb.com/eric/tools/css/reset/ + + You can probably argue if it is needed here, or not, but for sure it + doesn't do any harm and gives us a fresh start. +*/ + +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, embed, +figure, figcaption, footer, header, hgroup, +menu, nav, output, ruby, section, summary, +time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; +} + +/* HTML5 display-role reset for older browsers */ +article, aside, details, figcaption, figure, +footer, header, hgroup, menu, nav, section { + display: block; +} +body { + line-height: 1; +} +ol, ul { + list-style: none; +} +blockquote, q { + quotes: none; +} +blockquote:before, blockquote:after, +q:before, q:after { + content: ''; + content: none; +} + +table { + border-collapse: collapse; + border-spacing: 0; +} + +/* + Now here is when interesting things start to appear. + + We set up styles with default font and nice gradient in the background. + And yes, there is a lot of repetition there because of -prefixes but we don't + want to leave anybody behind. +*/ +body { + font-family: 'PT Sans', sans-serif; + min-height: 740px; + + background: rgb(215, 215, 215); + background: -webkit-gradient(radial, 50% 50%, 0, 50% 50%, 500, from(rgb(240, 240, 240)), to(rgb(190, 190, 190))); + background: -webkit-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190)); + background: -moz-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190)); + background: -ms-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190)); + background: -o-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190)); + background: radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190)); +} + +/* + Now let's bring some text styles back ... +*/ +b, strong { font-weight: bold } +i, em { font-style: italic } + +/* + ... and give links a nice look. +*/ +a { + color: inherit; + text-decoration: none; + padding: 0 0.1em; + background: rgba(255,255,255,0.5); + text-shadow: -1px -1px 2px rgba(100,100,100,0.9); + border-radius: 0.2em; + + -webkit-transition: 0.5s; + -moz-transition: 0.5s; + -ms-transition: 0.5s; + -o-transition: 0.5s; + transition: 0.5s; +} + +a:hover, +a:focus { + background: rgba(255,255,255,1); + text-shadow: -1px -1px 2px rgba(100,100,100,0.5); +} + +/* + Because the main point behind the impress.js demo is to demo impress.js + we display a fallback message for users with browsers that don't support + all the features required by it. + + All of the content will be still fully accessible for them, but I want + them to know that they are missing something - that's what the demo is + about, isn't it? + + And then we hide the message, when support is detected in the browser. +*/ + +.fallback-message { + font-family: sans-serif; + line-height: 1.3; + + width: 780px; + padding: 10px 10px 0; + margin: 20px auto; + + border: 1px solid #E4C652; + border-radius: 10px; + background: #EEDC94; +} + +.fallback-message p { + margin-bottom: 10px; +} + +.impress-supported .fallback-message { + display: none; +} + +/* + Now let's style the presentation steps. + + We start with basics to make sure it displays correctly in everywhere ... +*/ + +.step { + position: relative; + width: 900px; + padding: 40px; + margin: 20px auto; + + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + -ms-box-sizing: border-box; + -o-box-sizing: border-box; + box-sizing: border-box; + + font-family: 'PT Serif', georgia, serif; + font-size: 48px; + line-height: 1.5; +} + +/* + ... and we enhance the styles for impress.js. + + Basically we remove the margin and make inactive steps a little bit transparent. +*/ +.impress-enabled .step { + margin: 0; + opacity: 0.3; + + -webkit-transition: opacity 1s; + -moz-transition: opacity 1s; + -ms-transition: opacity 1s; + -o-transition: opacity 1s; + transition: opacity 1s; +} + +.impress-enabled .step.active { opacity: 1 } + +/* + These 'slide' step styles were heavily inspired by HTML5 Slides: + http://html5slides.googlecode.com/svn/trunk/styles.css + + ;) + + They cover everything what you see on first three steps of the demo. +*/ +.slide { + display: block; + + width: 900px; + height: 700px; + padding: 40px 60px; + + background-color: white; + border: 1px solid rgba(0, 0, 0, .3); + border-radius: 10px; + box-shadow: 0 2px 6px rgba(0, 0, 0, .1); + + color: rgb(102, 102, 102); + text-shadow: 0 2px 2px rgba(0, 0, 0, .1); + + font-family: 'Open Sans', Arial, sans-serif; + font-size: 30px; + line-height: 36px; + letter-spacing: -1px; +} + +.slide q { + display: block; + font-size: 50px; + line-height: 72px; + + margin-top: 100px; +} + +.slide q strong { + white-space: nowrap; +} + +/* + And now we start to style each step separately. + + I agree that this may be not the most efficient, object-oriented and + scalable way of styling, but most of steps have quite a custom look + and typography tricks here and there, so they had to be styles separately. + + First is the title step with a big

          (no room for padding) and some + 3D positioning along Z axis. +*/ + +#title { + padding: 0; +} + +#title .try { + font-size: 64px; + position: absolute; + top: -0.5em; + left: 1.5em; + + -webkit-transform: translateZ(20px); + -moz-transform: translateZ(20px); + -ms-transform: translateZ(20px); + -o-transform: translateZ(20px); + transform: translateZ(20px); +} + +#title h1 { + font-size: 190px; + + -webkit-transform: translateZ(50px); + -moz-transform: translateZ(50px); + -ms-transform: translateZ(50px); + -o-transform: translateZ(50px); + transform: translateZ(50px); +} + +#title .footnote { + font-size: 32px; +} + +/* + Second step is nothing special, just a text with a link, so it doesn't need + any special styling. + + Let's move to 'big thoughts' with centered text and custom font sizes. +*/ +#big { + width: 600px; + text-align: center; + font-size: 60px; + line-height: 1; +} + +#big b { + display: block; + font-size: 250px; + line-height: 250px; +} + +#big .thoughts { + font-size: 90px; + line-height: 150px; +} + +/* + 'Tiny ideas' just need some tiny styling. +*/ +#tiny { + width: 500px; + text-align: center; +} + +/* + This step has some animated text ... +*/ +#ing { width: 500px } + +/* + ... so we define display to `inline-block` to enable transforms and + transition duration to 0.5s ... +*/ +#ing b { + display: inline-block; + -webkit-transition: 0.5s; + -moz-transition: 0.5s; + -ms-transition: 0.5s; + -o-transition: 0.5s; + transition: 0.5s; +} + +/* + ... and we want 'positioning` word to move up a bit when the step gets + `present` class ... +*/ +#ing.present .positioning { + -webkit-transform: translateY(-10px); + -moz-transform: translateY(-10px); + -ms-transform: translateY(-10px); + -o-transform: translateY(-10px); + transform: translateY(-10px); +} + +/* + ... 'rotating' to rotate quater of a second later ... +*/ +#ing.present .rotating { + -webkit-transform: rotate(-10deg); + -moz-transform: rotate(-10deg); + -ms-transform: rotate(-10deg); + -o-transform: rotate(-10deg); + transform: rotate(-10deg); + + -webkit-transition-delay: 0.25s; + -moz-transition-delay: 0.25s; + -ms-transition-delay: 0.25s; + -o-transition-delay: 0.25s; + transition-delay: 0.25s; +} + +/* + ... and 'scaling' to scale down after another quater of a second. +*/ +#ing.present .scaling { + -webkit-transform: scale(0.7); + -moz-transform: scale(0.7); + -ms-transform: scale(0.7); + -o-transform: scale(0.7); + transform: scale(0.7); + + -webkit-transition-delay: 0.5s; + -moz-transition-delay: 0.5s; + -ms-transition-delay: 0.5s; + -o-transition-delay: 0.5s; + transition-delay: 0.5s; +} + +/* + The 'imagination' step is again some boring font-sizing. +*/ + +#imagination { + width: 600px; +} + +#imagination .imagination { + font-size: 78px; +} + +/* + There is nothing really special about 'use the source, Luke' step, too, + except maybe of the Yoda background. + + As you can see below I've 'hard-coded' it in data URL. + That's not the best way to serve images, but because that's just this one + I decided it will be OK to have it this way. + + Just make sure you don't blindly copy this approach. +*/ +#source { + width: 700px; + padding-bottom: 300px; + + /* Yoda Icon :: Pixel Art from Star Wars http://www.pixeljoint.com/pixelart/1423.htm */ + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAARgAAAEYCAMAAACwUBm+AAAAAXNSR0IArs4c6QAAAKtQTFRFsAAAvbWSLUUrLEQqY1s8UYJMqJ1vNTEgOiIdIzYhjIFVLhsXZ6lgSEIsP2U8JhcCVzMsSXZEgXdOO145XJdWOl03LzAYMk4vSXNExr+hwcuxRTs1Qmk+RW9Am49eFRANQz4pUoNMQWc+OSMDTz0wLBsCNVMxa2NBOyUDUoNNSnlEWo9VRGxAVzYFl6tXCggHbLNmMUIcHhwTXkk5f3VNRT8wUT8xAAAACQocRBWFFwAAAAF0Uk5TAEDm2GYAAAPCSURBVHja7d3JctNAFIZRMwRCCGEmzPM8z/D+T8bu/ptbXXJFdij5fMt2Wuo+2UgqxVmtttq5WVotLzBgwIABAwYMGDCn0qVqbo69psPqVpWx+1XG5iaavF8wYMCAAQMGDBgwi4DJ6Y6qkxB1HNlcN3a92gbR5P2CAQMGDBgwYMCAWSxMlrU+UY5yu2l9okfV4bAxUVbf7TJnAwMGDBgwYMCAAbMLMHeqbGR82Zy+VR1Ht81nVca6R+UdTLaU24Ruzd3qM/e4yjnAgAEDBgwYMGDA7AJMd1l/3NRdVGcj3eX/2WEhCmDGxnM7yqygu8XIPjJj8iN/MGDAgAEDBgwYMAuDGb8q0RGlLCHLv1t9qDKWn3vdNHVuEI6HPaxO9Jo3GDBgwIABAwYMmIXBdC9ShGgMk+XnkXUeuGcsP/e1+lhNnZsL/G5Vs3OAAQMGDBgwYMCAWSxMR3SzOmraG5atdy9wZKzb+vg16qyqe2FltbnAgAEDBgwYMGDALAxmTJSuN3WA76rnVca6GTnemGN1WoEBAwYMGDBgwIBZGMxUomy4+xO899V4LAg5Xnc2MGDAgAEDBgwYMGA218Wq+2K1LDqvY9xZu8zN8fICdM6btYABAwYMGDBgwIABMzfH0+pGU5afze2tXebmeAfVz+p8BQYMGDBgwIABAwbMPBzZ+oWmfJrln1273FhkbHzee9WWbw7AgAEDBgwYMGDALAKm43hcdctKgblcPamOhuXnXlY5Xs6bsW4FGyQCAwYMGDBgwIABswiYMceZKgvMo+h8mrHLTdn676rj+FEFoTtHd8MwOxEYMGDAgAEDBgyYRcBM5UhXqiymW3R3c9ARhWO/OmjqfjVZy+xEYMCAAQMGDBgwYBYG073OnCV0RFNhMhaOa9WfKmOB6XjHMN1tQmaAAQMGDBgwYMCA2VWY7vXjz1U4croAzgPztwIDBgwYMGDAgAEDZhswh035NBw59Dww3RgYMGDAgAEDBgwYMJuD6f4tXT7NUqfCdBvZLkxXdgQGDBgwYMCAAQNmt2DGj8WzwAfV/w7T/aq7mxwwYMCAAQMGDBgwuwqTOo7uTwTngflSzQ3TdaJvAwEDBgwYMGDAgAED5gSvgbyo5oHZ4Pc+gwEDBgwYMGDAgAEzhOm+5G0qTGaAAQMGDBgwYMCAAXNaMOcnls3tNwWm+zRzp54NDBgwYMCAAQMGDJh5YNL36k1TLuGvVq+qnKMbS5n7tulT9asCAwYMGDBgwIABA2ZumKuztLnjgQEDBgwYMGDAgNl5mH/4/ltKA6vBNAAAAABJRU5ErkJggg==); + background-position: bottom right; + background-repeat: no-repeat; +} + +#source q { + font-size: 60px; +} + +/* + And the "it's in 3D" step again brings some 3D typography - just for fun. + + Because we want to position elements in 3D we set transform-style to + `preserve-3d` on the paragraph. + It is not needed by webkit browsers, but it is in Firefox. It's hard to say + which behaviour is correct as 3D transforms spec is not very clear about it. +*/ +#its-in-3d p { + -webkit-transform-style: preserve-3d; + -moz-transform-style: preserve-3d; /* Y U need this Firefox?! */ + -ms-transform-style: preserve-3d; + -o-transform-style: preserve-3d; + transform-style: preserve-3d; +} + +/* + Below we position each word separately along Z axis and we want it to transition + to default position in 0.5s when the step gets `present` class. + + Quite a simple idea, but lot's of styles and prefixes. +*/ +#its-in-3d span, +#its-in-3d b { + display: inline-block; + -webkit-transform: translateZ(40px); + -moz-transform: translateZ(40px); + -ms-transform: translateZ(40px); + -o-transform: translateZ(40px); + transform: translateZ(40px); + + -webkit-transition: 0.5s; + -moz-transition: 0.5s; + -ms-transition: 0.5s; + -o-transition: 0.5s; + transition: 0.5s; +} + +#its-in-3d .have { + -webkit-transform: translateZ(-40px); + -moz-transform: translateZ(-40px); + -ms-transform: translateZ(-40px); + -o-transform: translateZ(-40px); + transform: translateZ(-40px); +} + +#its-in-3d .you { + -webkit-transform: translateZ(20px); + -moz-transform: translateZ(20px); + -ms-transform: translateZ(20px); + -o-transform: translateZ(20px); + transform: translateZ(20px); +} + +#its-in-3d .noticed { + -webkit-transform: translateZ(-40px); + -moz-transform: translateZ(-40px); + -ms-transform: translateZ(-40px); + -o-transform: translateZ(-40px); + transform: translateZ(-40px); +} + +#its-in-3d .its { + -webkit-transform: translateZ(60px); + -moz-transform: translateZ(60px); + -ms-transform: translateZ(60px); + -o-transform: translateZ(60px); + transform: translateZ(60px); +} + +#its-in-3d .in { + -webkit-transform: translateZ(-10px); + -moz-transform: translateZ(-10px); + -ms-transform: translateZ(-10px); + -o-transform: translateZ(-10px); + transform: translateZ(-10px); +} + +#its-in-3d .footnote { + font-size: 32px; + + -webkit-transform: translateZ(-10px); + -moz-transform: translateZ(-10px); + -ms-transform: translateZ(-10px); + -o-transform: translateZ(-10px); + transform: translateZ(-10px); +} + +#its-in-3d.present span, +#its-in-3d.present b { + -webkit-transform: translateZ(0px); + -moz-transform: translateZ(0px); + -ms-transform: translateZ(0px); + -o-transform: translateZ(0px); + transform: translateZ(0px); +} + +/* + The last step is an overview. + There is no content in it, so we make sure it's not visible because we want + to be able to click on other steps. + +*/ +#overview { display: none } + +/* + We also make other steps visible and give them a pointer cursor using the + `impress-on-` class. +*/ +.impress-on-overview .step { + opacity: 1; + cursor: pointer; +} + + +/* + Now, when we have all the steps styled let's give users a hint how to navigate + around the presentation. + + The best way to do this would be to use JavaScript, show a delayed hint for a + first time users, then hide it and store a status in cookie or localStorage... + + But I wanted to have some CSS fun and avoid additional scripting... + + Let me explain it first, so maybe the transition magic will be more readable + when you read the code. + + First of all I wanted the hint to appear only when user is idle for a while. + You can't detect the 'idle' state in CSS, but I delayed a appearing of the + hint by 5s using transition-delay. + + You also can't detect in CSS if the user is a first-time visitor, so I had to + make an assumption that I'll only show the hint on the first step. And when + the step is changed hide the hint, because I can assume that user already + knows how to navigate. + + To summarize it - hint is shown when the user is on the first step for longer + than 5 seconds. + + The other problem I had was caused by the fact that I wanted the hint to fade + in and out. It can be easily achieved by transitioning the opacity property. + But that also meant that the hint was always on the screen, even if totally + transparent. It covered part of the screen and you couldn't correctly clicked + through it. + Unfortunately you cannot transition between display `block` and `none` in pure + CSS, so I needed a way to not only fade out the hint but also move it out of + the screen. + + I solved this problem by positioning the hint below the bottom of the screen + with CSS transform and moving it up to show it. But I also didn't want this move + to be visible. I wanted the hint only to fade in and out visually, so I delayed + the fade in transition, so it starts when the hint is already in its correct + position on the screen. + + I know, it sounds complicated ... maybe it would be easier with the code? +*/ + +.hint { + /* + We hide the hint until presentation is started and from browsers not supporting + impress.js, as they will have a linear scrollable view ... + */ + display: none; + + /* + ... and give it some fixed position and nice styles. + */ + position: fixed; + left: 0; + right: 0; + bottom: 200px; + + background: rgba(0,0,0,0.5); + color: #EEE; + text-align: center; + + font-size: 50px; + padding: 20px; + + z-index: 100; + + /* + By default we don't want the hint to be visible, so we make it transparent ... + */ + opacity: 0; + + /* + ... and position it below the bottom of the screen (relative to it's fixed position) + */ + -webkit-transform: translateY(400px); + -moz-transform: translateY(400px); + -ms-transform: translateY(400px); + -o-transform: translateY(400px); + transform: translateY(400px); + + /* + Now let's imagine that the hint is visible and we want to fade it out and move out + of the screen. + + So we define the transition on the opacity property with 1s duration and another + transition on transform property delayed by 1s so it will happen after the fade out + on opacity finished. + + This way user will not see the hint moving down. + */ + -webkit-transition: opacity 1s, -webkit-transform 0.5s 1s; + -moz-transition: opacity 1s, -moz-transform 0.5s 1s; + -ms-transition: opacity 1s, -ms-transform 0.5s 1s; + -o-transition: opacity 1s, -o-transform 0.5s 1s; + transition: opacity 1s, transform 0.5s 1s; +} + +/* + Now we 'enable' the hint when presentation is initialized ... +*/ +.impress-enabled .hint { display: block } + +/* + ... and we will show it when the first step (with id 'bored') is active. +*/ +.impress-on-bored .hint { + /* + We remove the transparency and position the hint in its default fixed + position. + */ + opacity: 1; + + -webkit-transform: translateY(0px); + -moz-transform: translateY(0px); + -ms-transform: translateY(0px); + -o-transform: translateY(0px); + transform: translateY(0px); + + /* + Now for fade in transition we have the oposite situation from the one + above. + + First after 4.5s delay we animate the transform property to move the hint + into its correct position and after that we fade it in with opacity + transition. + */ + -webkit-transition: opacity 1s 5s, -webkit-transform 0.5s 4.5s; + -moz-transition: opacity 1s 5s, -moz-transform 0.5s 4.5s; + -ms-transition: opacity 1s 5s, -ms-transform 0.5s 4.5s; + -o-transition: opacity 1s 5s, -o-transform 0.5s 4.5s; + transition: opacity 1s 5s, transform 0.5s 4.5s; +} + +/* + And as the last thing there is a workaround for quite strange bug. + It happens a lot in Chrome. I don't remember if I've seen it in Firefox. + + Sometimes the element positioned in 3D (especially when it's moved back + along Z axis) is not clickable, because it falls 'behind' the + element. + + To prevent this, I decided to make non clickable by setting + pointer-events property to `none` value. + Value if this property is inherited, so to make everything else clickable + I bring it back on the #impress element. + + If you want to know more about `pointer-events` here are some docs: + https://developer.mozilla.org/en/CSS/pointer-events + + There is one very important thing to notice about this workaround - it makes + everything 'unclickable' except what's in #impress element. + + So use it wisely ... or don't use at all. +*/ +.impress-enabled { pointer-events: none } +.impress-enabled #impress { pointer-events: auto } + +/* + There is one funny thing I just realized. + + Thanks to this workaround above everything except #impress element is invisible + for click events. That means that the hint element is also not clickable. + So basically all of this transforms and delayed transitions trickery was probably + not needed at all... + + But it was fun to learn about it, wasn't it? +*/ + +/* + That's all I have for you in this file. + Thanks for reading. I hope you enjoyed it at least as much as I enjoyed writing it + for you. +*/ diff --git a/apps/impress/css/style.css b/apps/impress/css/style.css new file mode 100755 index 0000000000..4c7f60ef84 --- /dev/null +++ b/apps/impress/css/style.css @@ -0,0 +1,14 @@ +#emptyfolder { position:absolute; margin:10em 0 0 10em; font-size:1.5em; font-weight:bold; color:#888; text-shadow:#fff 0 1px 0; } + +.impresslist { margin-top:40px; padding:5px; width:100%;} +.impresslist tr:hover { backgound-color:#ccc; } +.impresslist tr td { padding:5px; } + +.docu { position: absolute; right: 13.5em; top: 0em; } + +#documentation { margin: 20px; } +#documentation h1 { font-size:1.8em; font-weight:bold; color:#888; } +#documentation h2 { font-size:1.5em; font-weight:bold; color:#888; } +#documentation h2 { font-size:1.2em; font-weight:bold; color:#888; } + +.examplecode { font-size:0.9em; font-family:"Courier New","Courier"; background-color:#eee; color:#333; width:90%; height:300px; padding:10px;} \ No newline at end of file diff --git a/apps/impress/documentation.php b/apps/impress/documentation.php new file mode 100755 index 0000000000..17a97432a4 --- /dev/null +++ b/apps/impress/documentation.php @@ -0,0 +1,34 @@ +. + * + */ + +require_once('lib/impress.php'); + +OCP\User::checkLoggedIn(); +OCP\App::setActiveNavigationEntry( 'impress_index' ); + + + +$tmpl = new OCP\Template('impress', 'documentation', 'user'); +$tmpl->printPage(); + + diff --git a/apps/impress/img/impress.png b/apps/impress/img/impress.png new file mode 100644 index 0000000000..23e14fc623 Binary files /dev/null and b/apps/impress/img/impress.png differ diff --git a/apps/impress/img/impress.svg b/apps/impress/img/impress.svg new file mode 100755 index 0000000000..af1cf89d42 --- /dev/null +++ b/apps/impress/img/impress.svg @@ -0,0 +1,1718 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/impress/img/impressbig.png b/apps/impress/img/impressbig.png new file mode 100644 index 0000000000..3c8d1632b3 Binary files /dev/null and b/apps/impress/img/impressbig.png differ diff --git a/apps/impress/img/impressbig.svg b/apps/impress/img/impressbig.svg new file mode 100755 index 0000000000..a4140e4ae1 --- /dev/null +++ b/apps/impress/img/impressbig.svg @@ -0,0 +1,1730 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/impress/index.php b/apps/impress/index.php new file mode 100755 index 0000000000..c7213d8661 --- /dev/null +++ b/apps/impress/index.php @@ -0,0 +1,37 @@ +. + * + */ + +require_once('lib/impress.php'); + +OCP\User::checkLoggedIn(); +OCP\App::setActiveNavigationEntry( 'impress_index' ); + + + +$list=\OCA_Impress\Storage::getPresentations(); + +$tmpl = new OCP\Template('impress', 'presentations', 'user'); +$tmpl->assign('list', $list); +$tmpl->printPage(); + + diff --git a/apps/impress/js/impress.js b/apps/impress/js/impress.js new file mode 100644 index 0000000000..224a5e2e7e --- /dev/null +++ b/apps/impress/js/impress.js @@ -0,0 +1,800 @@ +/** + * impress.js + * + * impress.js is a presentation tool based on the power of CSS3 transforms and transitions + * in modern browsers and inspired by the idea behind prezi.com. + * + * + * Copyright 2011-2012 Bartek Szopka (@bartaz) + * + * Released under the MIT and GPL Licenses. + * + * ------------------------------------------------ + * author: Bartek Szopka + * version: 0.5.3 + * url: http://bartaz.github.com/impress.js/ + * source: http://github.com/bartaz/impress.js/ + */ + +/*jshint bitwise:true, curly:true, eqeqeq:true, forin:true, latedef:true, newcap:true, + noarg:true, noempty:true, undef:true, strict:true, browser:true */ + +// You are one of those who like to know how thing work inside? +// Let me show you the cogs that make impress.js run... +(function ( document, window ) { + 'use strict'; + + // HELPER FUNCTIONS + + // `pfx` is a function that takes a standard CSS property name as a parameter + // and returns it's prefixed version valid for current browser it runs in. + // The code is heavily inspired by Modernizr http://www.modernizr.com/ + var pfx = (function () { + + var style = document.createElement('dummy').style, + prefixes = 'Webkit Moz O ms Khtml'.split(' '), + memory = {}; + + return function ( prop ) { + if ( typeof memory[ prop ] === "undefined" ) { + + var ucProp = prop.charAt(0).toUpperCase() + prop.substr(1), + props = (prop + ' ' + prefixes.join(ucProp + ' ') + ucProp).split(' '); + + memory[ prop ] = null; + for ( var i in props ) { + if ( style[ props[i] ] !== undefined ) { + memory[ prop ] = props[i]; + break; + } + } + + } + + return memory[ prop ]; + }; + + })(); + + // `arraify` takes an array-like object and turns it into real Array + // to make all the Array.prototype goodness available. + var arrayify = function ( a ) { + return [].slice.call( a ); + }; + + // `css` function applies the styles given in `props` object to the element + // given as `el`. It runs all property names through `pfx` function to make + // sure proper prefixed version of the property is used. + var css = function ( el, props ) { + var key, pkey; + for ( key in props ) { + if ( props.hasOwnProperty(key) ) { + pkey = pfx(key); + if ( pkey !== null ) { + el.style[pkey] = props[key]; + } + } + } + return el; + }; + + // `toNumber` takes a value given as `numeric` parameter and tries to turn + // it into a number. If it is not possible it returns 0 (or other value + // given as `fallback`). + var toNumber = function (numeric, fallback) { + return isNaN(numeric) ? (fallback || 0) : Number(numeric); + }; + + // `byId` returns element with given `id` - you probably have guessed that ;) + var byId = function ( id ) { + return document.getElementById(id); + }; + + // `$` returns first element for given CSS `selector` in the `context` of + // the given element or whole document. + var $ = function ( selector, context ) { + context = context || document; + return context.querySelector(selector); + }; + + // `$$` return an array of elements for given CSS `selector` in the `context` of + // the given element or whole document. + var $$ = function ( selector, context ) { + context = context || document; + return arrayify( context.querySelectorAll(selector) ); + }; + + // `triggerEvent` builds a custom DOM event with given `eventName` and `detail` data + // and triggers it on element given as `el`. + var triggerEvent = function (el, eventName, detail) { + var event = document.createEvent("CustomEvent"); + event.initCustomEvent(eventName, true, true, detail); + el.dispatchEvent(event); + }; + + // `translate` builds a translate transform string for given data. + var translate = function ( t ) { + return " translate3d(" + t.x + "px," + t.y + "px," + t.z + "px) "; + }; + + // `rotate` builds a rotate transform string for given data. + // By default the rotations are in X Y Z order that can be reverted by passing `true` + // as second parameter. + var rotate = function ( r, revert ) { + var rX = " rotateX(" + r.x + "deg) ", + rY = " rotateY(" + r.y + "deg) ", + rZ = " rotateZ(" + r.z + "deg) "; + + return revert ? rZ+rY+rX : rX+rY+rZ; + }; + + // `scale` builds a scale transform string for given data. + var scale = function ( s ) { + return " scale(" + s + ") "; + }; + + // `perspective` builds a perspective transform string for given data. + var perspective = function ( p ) { + return " perspective(" + p + "px) "; + }; + + // `getElementFromHash` returns an element located by id from hash part of + // window location. + var getElementFromHash = function () { + // get id from url # by removing `#` or `#/` from the beginning, + // so both "fallback" `#slide-id` and "enhanced" `#/slide-id` will work + return byId( window.location.hash.replace(/^#\/?/,"") ); + }; + + // `computeWindowScale` counts the scale factor between window size and size + // defined for the presentation in the config. + var computeWindowScale = function ( config ) { + var hScale = window.innerHeight / config.height, + wScale = window.innerWidth / config.width, + scale = hScale > wScale ? wScale : hScale; + + if (config.maxScale && scale > config.maxScale) { + scale = config.maxScale; + } + + if (config.minScale && scale < config.minScale) { + scale = config.minScale; + } + + return scale; + }; + + // CHECK SUPPORT + var body = document.body; + + var ua = navigator.userAgent.toLowerCase(); + var impressSupported = + // browser should support CSS 3D transtorms + ( pfx("perspective") !== null ) && + + // and `classList` and `dataset` APIs + ( body.classList ) && + ( body.dataset ) && + + // but some mobile devices need to be blacklisted, + // because their CSS 3D support or hardware is not + // good enough to run impress.js properly, sorry... + ( ua.search(/(iphone)|(ipod)|(android)/) === -1 ); + + if (!impressSupported) { + // we can't be sure that `classList` is supported + body.className += " impress-not-supported "; + } else { + body.classList.remove("impress-not-supported"); + body.classList.add("impress-supported"); + } + + // GLOBALS AND DEFAULTS + + // This is were the root elements of all impress.js instances will be kept. + // Yes, this means you can have more than one instance on a page, but I'm not + // sure if it makes any sense in practice ;) + var roots = {}; + + // some default config values. + var defaults = { + width: 1024, + height: 768, + maxScale: 1, + minScale: 0, + + perspective: 1000, + + transitionDuration: 1000 + }; + + // it's just an empty function ... and a useless comment. + var empty = function () { return false; }; + + // IMPRESS.JS API + + // And that's where interesting things will start to happen. + // It's the core `impress` function that returns the impress.js API + // for a presentation based on the element with given id ('impress' + // by default). + var impress = window.impress = function ( rootId ) { + + // If impress.js is not supported by the browser return a dummy API + // it may not be a perfect solution but we return early and avoid + // running code that may use features not implemented in the browser. + if (!impressSupported) { + return { + init: empty, + goto: empty, + prev: empty, + next: empty + }; + } + + rootId = rootId || "impress"; + + // if given root is already initialized just return the API + if (roots["impress-root-" + rootId]) { + return roots["impress-root-" + rootId]; + } + + // data of all presentation steps + var stepsData = {}; + + // element of currently active step + var activeStep = null; + + // current state (position, rotation and scale) of the presentation + var currentState = null; + + // array of step elements + var steps = null; + + // configuration options + var config = null; + + // scale factor of the browser window + var windowScale = null; + + // root presentation elements + var root = byId( rootId ); + var canvas = document.createElement("div"); + + var initialized = false; + + // STEP EVENTS + // + // There are currently two step events triggered by impress.js + // `impress:stepenter` is triggered when the step is shown on the + // screen (the transition from the previous one is finished) and + // `impress:stepleave` is triggered when the step is left (the + // transition to next step just starts). + + // reference to last entered step + var lastEntered = null; + + // `onStepEnter` is called whenever the step element is entered + // but the event is triggered only if the step is different than + // last entered step. + var onStepEnter = function (step) { + if (lastEntered !== step) { + triggerEvent(step, "impress:stepenter"); + lastEntered = step; + } + }; + + // `onStepLeave` is called whenever the step element is left + // but the event is triggered only if the step is the same as + // last entered step. + var onStepLeave = function (step) { + if (lastEntered === step) { + triggerEvent(step, "impress:stepleave"); + lastEntered = null; + } + }; + + // `initStep` initializes given step element by reading data from its + // data attributes and setting correct styles. + var initStep = function ( el, idx ) { + var data = el.dataset, + step = { + translate: { + x: toNumber(data.x), + y: toNumber(data.y), + z: toNumber(data.z) + }, + rotate: { + x: toNumber(data.rotateX), + y: toNumber(data.rotateY), + z: toNumber(data.rotateZ || data.rotate) + }, + scale: toNumber(data.scale, 1), + el: el + }; + + if ( !el.id ) { + el.id = "step-" + (idx + 1); + } + + stepsData["impress-" + el.id] = step; + + css(el, { + position: "absolute", + transform: "translate(-50%,-50%)" + + translate(step.translate) + + rotate(step.rotate) + + scale(step.scale), + transformStyle: "preserve-3d" + }); + }; + + // `init` API function that initializes (and runs) the presentation. + var init = function () { + if (initialized) { return; } + + // First we set up the viewport for mobile devices. + // For some reason iPad goes nuts when it is not done properly. + var meta = $("meta[name='viewport']") || document.createElement("meta"); + meta.content = "width=device-width, minimum-scale=1, maximum-scale=1, user-scalable=no"; + if (meta.parentNode !== document.head) { + meta.name = 'viewport'; + document.head.appendChild(meta); + } + + // initialize configuration object + var rootData = root.dataset; + config = { + width: toNumber( rootData.width, defaults.width ), + height: toNumber( rootData.height, defaults.height ), + maxScale: toNumber( rootData.maxScale, defaults.maxScale ), + minScale: toNumber( rootData.minScale, defaults.minScale ), + perspective: toNumber( rootData.perspective, defaults.perspective ), + transitionDuration: toNumber( rootData.transitionDuration, defaults.transitionDuration ) + }; + + windowScale = computeWindowScale( config ); + + // wrap steps with "canvas" element + arrayify( root.childNodes ).forEach(function ( el ) { + canvas.appendChild( el ); + }); + root.appendChild(canvas); + + // set initial styles + document.documentElement.style.height = "100%"; + + css(body, { + height: "100%", + overflow: "hidden" + }); + + var rootStyles = { + position: "absolute", + transformOrigin: "top left", + transition: "all 0s ease-in-out", + transformStyle: "preserve-3d" + }; + + css(root, rootStyles); + css(root, { + top: "50%", + left: "50%", + transform: perspective( config.perspective/windowScale ) + scale( windowScale ) + }); + css(canvas, rootStyles); + + body.classList.remove("impress-disabled"); + body.classList.add("impress-enabled"); + + // get and init steps + steps = $$(".step", root); + steps.forEach( initStep ); + + // set a default initial state of the canvas + currentState = { + translate: { x: 0, y: 0, z: 0 }, + rotate: { x: 0, y: 0, z: 0 }, + scale: 1 + }; + + initialized = true; + + triggerEvent(root, "impress:init", { api: roots[ "impress-root-" + rootId ] }); + }; + + // `getStep` is a helper function that returns a step element defined by parameter. + // If a number is given, step with index given by the number is returned, if a string + // is given step element with such id is returned, if DOM element is given it is returned + // if it is a correct step element. + var getStep = function ( step ) { + if (typeof step === "number") { + step = step < 0 ? steps[ steps.length + step] : steps[ step ]; + } else if (typeof step === "string") { + step = byId(step); + } + return (step && step.id && stepsData["impress-" + step.id]) ? step : null; + }; + + // used to reset timeout for `impress:stepenter` event + var stepEnterTimeout = null; + + // `goto` API function that moves to step given with `el` parameter (by index, id or element), + // with a transition `duration` optionally given as second parameter. + var goto = function ( el, duration ) { + + if ( !initialized || !(el = getStep(el)) ) { + // presentation not initialized or given element is not a step + return false; + } + + // Sometimes it's possible to trigger focus on first link with some keyboard action. + // Browser in such a case tries to scroll the page to make this element visible + // (even that body overflow is set to hidden) and it breaks our careful positioning. + // + // So, as a lousy (and lazy) workaround we will make the page scroll back to the top + // whenever slide is selected + // + // If you are reading this and know any better way to handle it, I'll be glad to hear about it! + window.scrollTo(0, 0); + + var step = stepsData["impress-" + el.id]; + + if ( activeStep ) { + activeStep.classList.remove("active"); + body.classList.remove("impress-on-" + activeStep.id); + } + el.classList.add("active"); + + body.classList.add("impress-on-" + el.id); + + // compute target state of the canvas based on given step + var target = { + rotate: { + x: -step.rotate.x, + y: -step.rotate.y, + z: -step.rotate.z + }, + translate: { + x: -step.translate.x, + y: -step.translate.y, + z: -step.translate.z + }, + scale: 1 / step.scale + }; + + // Check if the transition is zooming in or not. + // + // This information is used to alter the transition style: + // when we are zooming in - we start with move and rotate transition + // and the scaling is delayed, but when we are zooming out we start + // with scaling down and move and rotation are delayed. + var zoomin = target.scale >= currentState.scale; + + duration = toNumber(duration, config.transitionDuration); + var delay = (duration / 2); + + // if the same step is re-selected, force computing window scaling, + // because it is likely to be caused by window resize + if (el === activeStep) { + windowScale = computeWindowScale(config); + } + + var targetScale = target.scale * windowScale; + + // trigger leave of currently active element (if it's not the same step again) + if (activeStep && activeStep !== el) { + onStepLeave(activeStep); + } + + // Now we alter transforms of `root` and `canvas` to trigger transitions. + // + // And here is why there are two elements: `root` and `canvas` - they are + // being animated separately: + // `root` is used for scaling and `canvas` for translate and rotations. + // Transitions on them are triggered with different delays (to make + // visually nice and 'natural' looking transitions), so we need to know + // that both of them are finished. + css(root, { + // to keep the perspective look similar for different scales + // we need to 'scale' the perspective, too + transform: perspective( config.perspective / targetScale ) + scale( targetScale ), + transitionDuration: duration + "ms", + transitionDelay: (zoomin ? delay : 0) + "ms" + }); + + css(canvas, { + transform: rotate(target.rotate, true) + translate(target.translate), + transitionDuration: duration + "ms", + transitionDelay: (zoomin ? 0 : delay) + "ms" + }); + + // Here is a tricky part... + // + // If there is no change in scale or no change in rotation and translation, it means there was actually + // no delay - because there was no transition on `root` or `canvas` elements. + // We want to trigger `impress:stepenter` event in the correct moment, so here we compare the current + // and target values to check if delay should be taken into account. + // + // I know that this `if` statement looks scary, but it's pretty simple when you know what is going on + // - it's simply comparing all the values. + if ( currentState.scale === target.scale || + (currentState.rotate.x === target.rotate.x && currentState.rotate.y === target.rotate.y && + currentState.rotate.z === target.rotate.z && currentState.translate.x === target.translate.x && + currentState.translate.y === target.translate.y && currentState.translate.z === target.translate.z) ) { + delay = 0; + } + + // store current state + currentState = target; + activeStep = el; + + // And here is where we trigger `impress:stepenter` event. + // We simply set up a timeout to fire it taking transition duration (and possible delay) into account. + // + // I really wanted to make it in more elegant way. The `transitionend` event seemed to be the best way + // to do it, but the fact that I'm using transitions on two separate elements and that the `transitionend` + // event is only triggered when there was a transition (change in the values) caused some bugs and + // made the code really complicated, cause I had to handle all the conditions separately. And it still + // needed a `setTimeout` fallback for the situations when there is no transition at all. + // So I decided that I'd rather make the code simpler than use shiny new `transitionend`. + // + // If you want learn something interesting and see how it was done with `transitionend` go back to + // version 0.5.2 of impress.js: http://github.com/bartaz/impress.js/blob/0.5.2/js/impress.js + window.clearTimeout(stepEnterTimeout); + stepEnterTimeout = window.setTimeout(function() { + onStepEnter(activeStep); + }, duration + delay); + + return el; + }; + + // `prev` API function goes to previous step (in document order) + var prev = function () { + var prev = steps.indexOf( activeStep ) - 1; + prev = prev >= 0 ? steps[ prev ] : steps[ steps.length-1 ]; + + return goto(prev); + }; + + // `next` API function goes to next step (in document order) + var next = function () { + var next = steps.indexOf( activeStep ) + 1; + next = next < steps.length ? steps[ next ] : steps[ 0 ]; + + return goto(next); + }; + + // Adding some useful classes to step elements. + // + // All the steps that have not been shown yet are given `future` class. + // When the step is entered the `future` class is removed and the `present` + // class is given. When the step is left `present` class is replaced with + // `past` class. + // + // So every step element is always in one of three possible states: + // `future`, `present` and `past`. + // + // There classes can be used in CSS to style different types of steps. + // For example the `present` class can be used to trigger some custom + // animations when step is shown. + root.addEventListener("impress:init", function(){ + // STEP CLASSES + steps.forEach(function (step) { + step.classList.add("future"); + }); + + root.addEventListener("impress:stepenter", function (event) { + event.target.classList.remove("past"); + event.target.classList.remove("future"); + event.target.classList.add("present"); + }, false); + + root.addEventListener("impress:stepleave", function (event) { + event.target.classList.remove("present"); + event.target.classList.add("past"); + }, false); + + }, false); + + // Adding hash change support. + root.addEventListener("impress:init", function(){ + + // last hash detected + var lastHash = ""; + + // `#/step-id` is used instead of `#step-id` to prevent default browser + // scrolling to element in hash. + // + // And it has to be set after animation finishes, because in Chrome it + // makes transtion laggy. + // BUG: http://code.google.com/p/chromium/issues/detail?id=62820 + root.addEventListener("impress:stepenter", function (event) { + window.location.hash = lastHash = "#/" + event.target.id; + }, false); + + window.addEventListener("hashchange", function () { + // When the step is entered hash in the location is updated + // (just few lines above from here), so the hash change is + // triggered and we would call `goto` again on the same element. + // + // To avoid this we store last entered hash and compare. + if (window.location.hash !== lastHash) { + goto( getElementFromHash() ); + } + }, false); + + // START + // by selecting step defined in url or first step of the presentation + goto(getElementFromHash() || steps[0], 0); + }, false); + + body.classList.add("impress-disabled"); + + // store and return API for given impress.js root element + return (roots[ "impress-root-" + rootId ] = { + init: init, + goto: goto, + next: next, + prev: prev + }); + + }; + + // flag that can be used in JS to check if browser have passed the support test + impress.supported = impressSupported; + +})(document, window); + +// NAVIGATION EVENTS + +// As you can see this part is separate from the impress.js core code. +// It's because these navigation actions only need what impress.js provides with +// its simple API. +// +// In future I think about moving it to make them optional, move to separate files +// and treat more like a 'plugins'. +(function ( document, window ) { + 'use strict'; + + // throttling function calls, by Remy Sharp + // http://remysharp.com/2010/07/21/throttling-function-calls/ + var throttle = function (fn, delay) { + var timer = null; + return function () { + var context = this, args = arguments; + clearTimeout(timer); + timer = setTimeout(function () { + fn.apply(context, args); + }, delay); + }; + }; + + // wait for impress.js to be initialized + document.addEventListener("impress:init", function (event) { + // Getting API from event data. + // So you don't event need to know what is the id of the root element + // or anything. `impress:init` event data gives you everything you + // need to control the presentation that was just initialized. + var api = event.detail.api; + + // KEYBOARD NAVIGATION HANDLERS + + // Prevent default keydown action when one of supported key is pressed. + document.addEventListener("keydown", function ( event ) { + if ( event.keyCode === 9 || ( event.keyCode >= 32 && event.keyCode <= 34 ) || (event.keyCode >= 37 && event.keyCode <= 40) ) { + event.preventDefault(); + } + }, false); + + // Trigger impress action (next or prev) on keyup. + + // Supported keys are: + // [space] - quite common in presentation software to move forward + // [up] [right] / [down] [left] - again common and natural addition, + // [pgdown] / [pgup] - often triggered by remote controllers, + // [tab] - this one is quite controversial, but the reason it ended up on + // this list is quite an interesting story... Remember that strange part + // in the impress.js code where window is scrolled to 0,0 on every presentation + // step, because sometimes browser scrolls viewport because of the focused element? + // Well, the [tab] key by default navigates around focusable elements, so clicking + // it very often caused scrolling to focused element and breaking impress.js + // positioning. I didn't want to just prevent this default action, so I used [tab] + // as another way to moving to next step... And yes, I know that for the sake of + // consistency I should add [shift+tab] as opposite action... + document.addEventListener("keyup", function ( event ) { + if ( event.keyCode === 9 || ( event.keyCode >= 32 && event.keyCode <= 34 ) || (event.keyCode >= 37 && event.keyCode <= 40) ) { + switch( event.keyCode ) { + case 33: // pg up + case 37: // left + case 38: // up + api.prev(); + break; + case 9: // tab + case 32: // space + case 34: // pg down + case 39: // right + case 40: // down + api.next(); + break; + } + + event.preventDefault(); + } + }, false); + + // delegated handler for clicking on the links to presentation steps + document.addEventListener("click", function ( event ) { + // event delegation with "bubbling" + // check if event target (or any of its parents is a link) + var target = event.target; + while ( (target.tagName !== "A") && + (target !== document.documentElement) ) { + target = target.parentNode; + } + + if ( target.tagName === "A" ) { + var href = target.getAttribute("href"); + + // if it's a link to presentation step, target this step + if ( href && href[0] === '#' ) { + target = document.getElementById( href.slice(1) ); + } + } + + if ( api.goto(target) ) { + event.stopImmediatePropagation(); + event.preventDefault(); + } + }, false); + + // delegated handler for clicking on step elements + document.addEventListener("click", function ( event ) { + var target = event.target; + // find closest step element that is not active + while ( !(target.classList.contains("step") && !target.classList.contains("active")) && + (target !== document.documentElement) ) { + target = target.parentNode; + } + + if ( api.goto(target) ) { + event.preventDefault(); + } + }, false); + + // touch handler to detect taps on the left and right side of the screen + // based on awesome work of @hakimel: https://github.com/hakimel/reveal.js + document.addEventListener("touchstart", function ( event ) { + if (event.touches.length === 1) { + var x = event.touches[0].clientX, + width = window.innerWidth * 0.3, + result = null; + + if ( x < width ) { + result = api.prev(); + } else if ( x > window.innerWidth - width ) { + result = api.next(); + } + + if (result) { + event.preventDefault(); + } + } + }, false); + + // rescale presentation when window is resized + window.addEventListener("resize", throttle(function () { + // force going to active step again, to trigger rescaling + api.goto( document.querySelector(".active"), 500 ); + }, 250), false); + + }, false); + +})(document, window); + +// THAT'S ALL FOLKS! +// +// Thanks for reading it all. +// Or thanks for scrolling down and reading the last part. +// +// I've learnt a lot when building impress.js and I hope this code and comments +// will help somebody learn at least some part of it. diff --git a/apps/impress/lib/impress.php b/apps/impress/lib/impress.php new file mode 100755 index 0000000000..ca33dbb232 --- /dev/null +++ b/apps/impress/lib/impress.php @@ -0,0 +1,118 @@ +. + * + */ + + + +/* + +Todo: + enable fullscreen presentation + +*/ + +namespace OCA_Impress; + +class Storage { + + public static function getPresentations() { + $presentations=array(); + $list=\OC_FileCache::searchByMime('text','impress' ); + foreach($list as $l) { + $info=pathinfo($l); + $size=\OC_Filesystem::filesize($l); + $mtime=\OC_Filesystem::filemtime($l); + + $entry=array('url'=>$l,'name'=>$info['filename'],'size'=>$size,'mtime'=>$mtime); + $presentations[]=$entry; + } + + + return $presentations; + } + + + public static function showHeader($title){ + + echo(' + + + + + + + '.$title.' + + + + + + + + + + +
          +

          Your browser does not support the features required by impress.js, so you are presented with a simplified version of this presentation.

          +

          For the best experience please use the latest Chrome, Safari or Firefox browser.

          +
          + + +
          + + + + '); + + } + + public static function showFooter(){ + + echo(' + +
          +

          Make fullscreen and use a spacebar or arrow keys to navigate

          +
          + + + + + + + + + '); + + } + + +} + +?> diff --git a/apps/impress/player.php b/apps/impress/player.php new file mode 100755 index 0000000000..12497de54c --- /dev/null +++ b/apps/impress/player.php @@ -0,0 +1,49 @@ +. +* +*/ + +require_once('lib/impress.php'); + +// Check if we are a user +OCP\User::checkLoggedIn(); + +$filename = strip_tags($_GET['file']); +$title = strip_tags($_GET['name']); + +if(!OC_Filesystem::file_exists($filename)){ + header("HTTP/1.0 404 Not Found"); + $tmpl = new OCP\Template( '', '404', 'guest' ); + $tmpl->assign('file',$filename); + $tmpl->printPage(); + exit; +} + +header('Content-Type: text/html',true); +OCP\Response::disableCaching(); +header('Content-Length: '.OC_Filesystem::filesize($filename)); + +@ob_end_clean(); + +\OCA_Impress\Storage::showHeader($title); +OC_Filesystem::readfile( $filename ); +\OCA_Impress\Storage::showFooter(); + diff --git a/apps/impress/templates/documentation.php b/apps/impress/templates/documentation.php new file mode 100755 index 0000000000..1995bece25 --- /dev/null +++ b/apps/impress/templates/documentation.php @@ -0,0 +1,223 @@ + + +
          + +

          Impress Documentation

          +
          + + +

          What is Impress?

          +Impress is an ownCloud application that can play presentation based on the amazing impress.js library. +

          + +

          How do I use it?

          +You have to define your presentation by writing a HTML file manually. But there are great examples and it isn´t that difficult. +Just put a HTML file with the file extension .impress into your ownCloud and it will show up here in the Impress app automatically. +If you click on it it opens up in a new windows. +

          + +

          How do I define the presentation?

          +The best way to learn it is to look at the example presenation. We suggest that you copy it and place it into your ownCloud with the name demo.impress and play around with it. You can edit in ownCloud directly with the internal text editor and play it here in the Impress app. +
          + + + + +

          + + + +

          Credits

          +Many thanks to Bartek Szopka for creating the wonderful impress.js library and also the demo presentation who made this app possible. +

          + + + +
          \ No newline at end of file diff --git a/apps/impress/templates/presentations.php b/apps/impress/templates/presentations.php new file mode 100755 index 0000000000..ab75802b35 --- /dev/null +++ b/apps/impress/templates/presentations.php @@ -0,0 +1,30 @@ + + '.$l->t('Documentation').' +
          +'); + + +if(empty($_['list'])) { + + echo('
          No Impress files are found in your ownCloud. Please upload a .impress file.
          '); + +}else{ + + echo(''); + foreach($_['list'] as $entry) { + + echo(''); + + } + echo('
          '.$entry['name'].''.\OCP\Util::formatDate($entry['mtime']).''.\OCP\Util::humanFileSize($entry['size']).'
          '); + + + +} + + diff --git a/apps/media/ajax/api.php b/apps/media/ajax/api.php index 37dc638019..7f5cdb22c1 100644 --- a/apps/media/ajax/api.php +++ b/apps/media/ajax/api.php @@ -24,8 +24,6 @@ header('Content-type: text/html; charset=UTF-8') ; OCP\JSON::checkAppEnabled('media'); -require_once(OC::$APPSROOT . '/apps/media/lib_collection.php'); -require_once(OC::$APPSROOT . '/apps/media/lib_scanner.php'); error_reporting(E_ALL); //no script error reporting because of getID3 @@ -48,6 +46,9 @@ if(!isset($arguments['album'])){ if(!isset($arguments['search'])){ $arguments['search']=''; } + +session_write_close(); + OC_MEDIA_COLLECTION::$uid=OCP\USER::getUser(); if($arguments['action']){ switch($arguments['action']){ @@ -105,6 +106,10 @@ if($arguments['action']){ @ob_end_clean(); $ftype=OC_Filesystem::getMimeType( $arguments['path'] ); + if(substr($ftype,0,5)!='audio' and $ftype!='application/ogg'){ + echo 'Not an audio file'; + exit(); + } $songId=OC_MEDIA_COLLECTION::getSongByPath($arguments['path']); OC_MEDIA_COLLECTION::registerPlay($songId); @@ -126,4 +131,3 @@ if($arguments['action']){ exit; } } -?> \ No newline at end of file diff --git a/apps/media/ajax/autoupdate.php b/apps/media/ajax/autoupdate.php index 3122c7e675..a5801f1a0e 100644 --- a/apps/media/ajax/autoupdate.php +++ b/apps/media/ajax/autoupdate.php @@ -23,11 +23,6 @@ header('Content-type: text/html; charset=UTF-8') ; -//no apps or filesystem -$RUNTIME_NOAPPS=true; -$RUNTIME_NOSETUPFS=true; - - OCP\JSON::checkAppEnabled('media'); $autoUpdate=(isset($_GET['autoupdate']) and $_GET['autoupdate']=='true'); @@ -35,4 +30,3 @@ $autoUpdate=(isset($_GET['autoupdate']) and $_GET['autoupdate']=='true'); OCP\Config::setUserValue(OCP\USER::getUser(),'media','autoupdate',(integer)$autoUpdate); OCP\JSON::success(array('data' => $autoUpdate)); -?> diff --git a/apps/media/appinfo/app.php b/apps/media/appinfo/app.php index 26cb204554..75015d627b 100644 --- a/apps/media/appinfo/app.php +++ b/apps/media/appinfo/app.php @@ -22,13 +22,26 @@ $l=OC_L10N::get('media'); -require_once('apps/media/lib_media.php'); +OC::$CLASSPATH['OC_MEDIA'] = 'media/lib_media.php'; +OC::$CLASSPATH['OC_MediaSearchProvider'] = 'media/lib_media.php'; +OC::$CLASSPATH['OC_MEDIA_COLLECTION'] = 'media/lib_collection.php'; +OC::$CLASSPATH['OC_MEDIA_SCANNER'] = 'media/lib_scanner.php'; + +//we need to have the sha256 hash of passwords for ampache +OCP\Util::connectHook('OC_User','post_login','OC_MEDIA','loginListener'); + +//connect to the filesystem for auto updating +OCP\Util::connectHook('OC_Filesystem','post_write','OC_MEDIA','updateFile'); + +//listen for file deletions to clean the database if a song is deleted +OCP\Util::connectHook('OC_Filesystem','post_delete','OC_MEDIA','deleteFile'); + +//list for file moves to update the database +OCP\Util::connectHook('OC_Filesystem','post_rename','OC_MEDIA','moveFile'); OCP\Util::addscript('media','loader'); OCP\App::registerPersonal('media','settings'); -OCP\App::register( array( 'order' => 3, 'id' => 'media', 'name' => 'Media' )); - OCP\App::addNavigationEntry(array('id' => 'media_index', 'order' => 2, 'href' => OCP\Util::linkTo('media', 'index.php'), 'icon' => OCP\Util::imagePath('core', 'places/music.svg'), 'name' => $l->t('Music'))); OC_Search::registerProvider('OC_MediaSearchProvider'); diff --git a/apps/media/appinfo/version b/apps/media/appinfo/version index e6adf3fc7b..44bb5d1f74 100644 --- a/apps/media/appinfo/version +++ b/apps/media/appinfo/version @@ -1 +1 @@ -0.4 \ No newline at end of file +0.4.1 \ No newline at end of file diff --git a/apps/media/index.php b/apps/media/index.php index 906d7bacb6..ae85abc8aa 100644 --- a/apps/media/index.php +++ b/apps/media/index.php @@ -28,9 +28,6 @@ OCP\User::checkLoggedIn(); OCP\App::checkAppEnabled('media'); -require_once(OC::$APPSROOT . '/apps/media/lib_collection.php'); -require_once(OC::$APPSROOT . '/apps/media/lib_scanner.php'); - OCP\Util::addscript('media','player'); OCP\Util::addscript('media','music'); OCP\Util::addscript('media','playlist'); @@ -43,5 +40,3 @@ OCP\App::setActiveNavigationEntry( 'media_index' ); $tmpl = new OCP\Template( 'media', 'music', 'user' ); $tmpl->printPage(); -?> - diff --git a/apps/media/js/loader.js b/apps/media/js/loader.js index 393f8ba914..ffe9c1cdd6 100644 --- a/apps/media/js/loader.js +++ b/apps/media/js/loader.js @@ -45,8 +45,8 @@ $(document).ready(function() { // FileActions.register('application/ogg','Add to playlist','',addAudio); if(typeof FileActions!=='undefined'){ - FileActions.register('audio','Play','',playAudio); - FileActions.register('application/ogg','','Play',playAudio); + FileActions.register('audio','Play', FileActions.PERMISSION_READ, '',playAudio); + FileActions.register('application/ogg', FileActions.PERMISSION_READ, '','Play',playAudio); FileActions.setDefault('audio','Play'); FileActions.setDefault('application/ogg','Play'); } diff --git a/apps/media/js/player.js b/apps/media/js/player.js index ad40683083..867ea80236 100644 --- a/apps/media/js/player.js +++ b/apps/media/js/player.js @@ -40,7 +40,7 @@ var PlayList={ PlayList.init(items[index].type,null); // init calls load that calls play }else{ PlayList.player.jPlayer("setMedia", items[PlayList.current]); - $(".jp-current-song").text(items[PlayList.current].name); + $(".jp-current-song").html(items[PlayList.current].name); items[index].playcount++; PlayList.player.jPlayer("play",time); if(index>0){ diff --git a/apps/media/l10n/ar.php b/apps/media/l10n/ar.php index a335f36a1f..655589df8a 100644 --- a/apps/media/l10n/ar.php +++ b/apps/media/l10n/ar.php @@ -1,5 +1,6 @@ "الموسيقى", +"Add album to playlist" => "أضف الالبوم الى القائمه", "Play" => "إلعب", "Pause" => "تجميد", "Previous" => "السابق", diff --git a/apps/media/l10n/bg_BG.php b/apps/media/l10n/bg_BG.php index 1b71b26a16..e6c3c02d17 100644 --- a/apps/media/l10n/bg_BG.php +++ b/apps/media/l10n/bg_BG.php @@ -1,5 +1,6 @@ "Музика", +"Add album to playlist" => "Добавяне на албума към списъка за изпълнение", "Play" => "Пусни", "Pause" => "Пауза", "Previous" => "Предишна", diff --git a/apps/media/l10n/eo.php b/apps/media/l10n/eo.php index 429247bbc3..084dbaa480 100644 --- a/apps/media/l10n/eo.php +++ b/apps/media/l10n/eo.php @@ -1,9 +1,10 @@ "Muziko", +"Add album to playlist" => "Aldoni albumon al ludlisto", "Play" => "Ludi", "Pause" => "Paŭzigi", -"Previous" => "Antaŭa", -"Next" => "Sekva", +"Previous" => "Maljena", +"Next" => "Jena", "Mute" => "Silentigi", "Unmute" => "Malsilentigi", "Rescan Collection" => "Reskani la aron", diff --git a/apps/media/l10n/fr.php b/apps/media/l10n/fr.php index c96e84d73f..313a918d02 100644 --- a/apps/media/l10n/fr.php +++ b/apps/media/l10n/fr.php @@ -1,6 +1,7 @@ "Musique", -"Play" => "Play", +"Add album to playlist" => "Ajouter l'album à la playlist", +"Play" => "Lire", "Pause" => "Pause", "Previous" => "Précédent", "Next" => "Suivant", diff --git a/apps/media/l10n/sv.php b/apps/media/l10n/sv.php index 75670a4851..1cf5497e7e 100644 --- a/apps/media/l10n/sv.php +++ b/apps/media/l10n/sv.php @@ -1,5 +1,6 @@ "Musik", +"Add album to playlist" => "Lägg till album till spellistan", "Play" => "Spela", "Pause" => "Paus", "Previous" => "Föregående", diff --git a/apps/media/l10n/uk.php b/apps/media/l10n/uk.php new file mode 100644 index 0000000000..4ac7abbf2b --- /dev/null +++ b/apps/media/l10n/uk.php @@ -0,0 +1,14 @@ + "Музика", +"Add album to playlist" => "Додати альбом до плейлиста", +"Play" => "Грати", +"Pause" => "Пауза", +"Previous" => "Попередній", +"Next" => "Наступний", +"Mute" => "Звук вкл.", +"Unmute" => "Звук викл.", +"Rescan Collection" => "Повторне сканування колекції", +"Artist" => "Виконавець", +"Album" => "Альбом", +"Title" => "Заголовок" +); diff --git a/apps/media/l10n/vi.php b/apps/media/l10n/vi.php new file mode 100644 index 0000000000..01942ba173 --- /dev/null +++ b/apps/media/l10n/vi.php @@ -0,0 +1,14 @@ + "Âm nhạc", +"Add album to playlist" => "Thêm album vào playlist", +"Play" => "Play", +"Pause" => "Tạm dừng", +"Previous" => "Trang trước", +"Next" => "Tiếp theo", +"Mute" => "Tắt", +"Unmute" => "Bật", +"Rescan Collection" => "Quét lại bộ sưu tập", +"Artist" => "Nghệ sỹ", +"Album" => "Album", +"Title" => "Tiêu đề" +); diff --git a/apps/media/l10n/zh_CN.GB2312.php b/apps/media/l10n/zh_CN.GB2312.php new file mode 100644 index 0000000000..de7e98acd9 --- /dev/null +++ b/apps/media/l10n/zh_CN.GB2312.php @@ -0,0 +1,14 @@ + "音乐", +"Add album to playlist" => "添加专辑到播放列表", +"Play" => "播放", +"Pause" => "暂停", +"Previous" => "前面的", +"Next" => "下一个", +"Mute" => "静音", +"Unmute" => "取消静音", +"Rescan Collection" => "重新扫描收藏", +"Artist" => "艺术家", +"Album" => "专辑", +"Title" => "标题" +); diff --git a/apps/media/lib/share/album.php b/apps/media/lib/share/album.php new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apps/media/lib/share/artist.php b/apps/media/lib/share/artist.php new file mode 100644 index 0000000000..7218fa1a27 --- /dev/null +++ b/apps/media/lib/share/artist.php @@ -0,0 +1,65 @@ +. +*/ + +class OC_Share_Backend_Artist extends OCP\Share_Backend { + + public function getSource($item, $uid) { + $query = OCP\DB::prepare('SELECT artist_id FROM *PREFIX*media_artists WHERE artist_id = ? AND song_user = ?'); + $result = $query->execute(array($item, $uid))->fetchRow(); + if (is_array($result)) { + return array('item' => $item, 'file' => $result['song_path']); + } + return false; + } + + public function generateTarget($item, $uid, $exclude = null) { + // TODO Make sure target path doesn't exist already + return '/Shared'.$item; + } + + public function formatItems($items, $format) { + $ids = array(); + foreach ($items as $id => $info) { + $ids[] = $id; + } + $ids = "'".implode("','", $ids)."'"; + switch ($format) { + case self::FORMAT_SOURCE_PATH: + $query = OCP\DB::prepare('SELECT path FROM *PREFIX*fscache WHERE id IN ('.$ids.')'); + return $query->execute()->fetchAll(); + case self::FORMAT_FILE_APP: + $query = OCP\DB::prepare('SELECT id, path, name, ctime, mtime, mimetype, size, encrypted, versioned, writable FROM *PREFIX*fscache WHERE id IN ('.$ids.')'); + $result = $query->execute(); + $files = array(); + while ($file = $result->fetchRow()) { + // Set target path + $file['path'] = $items[$file['id']]['item_target']; + $file['name'] = basename($file['path']); + // TODO Set permissions: $file['writable'] + $files[] = $file; + } + return $files; + } + } + +} + +?> \ No newline at end of file diff --git a/apps/media/lib/share/song.php b/apps/media/lib/share/song.php new file mode 100644 index 0000000000..fc69975f35 --- /dev/null +++ b/apps/media/lib/share/song.php @@ -0,0 +1,65 @@ +. +*/ + +class OC_Share_Backend_Song extends OCP\Share_Backend { + + public function getSource($item, $uid) { + $query = OCP\DB::prepare('SELECT song_path FROM *PREFIX*media_songs WHERE song_id = ? AND song_user = ?'); + $result = $query->execute(array($item, $uid))->fetchRow(); + if (is_array($result)) { + return array('item' => $item, 'file' => $result['song_path']); + } + return false; + } + + public function generateTarget($item, $uid, $exclude = null) { + // TODO Make sure target path doesn't exist already + return '/Shared'.$item; + } + + public function formatItems($items, $format) { + $ids = array(); + foreach ($items as $id => $info) { + $ids[] = $id; + } + $ids = "'".implode("','", $ids)."'"; + switch ($format) { + case self::FORMAT_SOURCE_PATH: + $query = OCP\DB::prepare('SELECT path FROM *PREFIX*fscache WHERE id IN ('.$ids.')'); + return $query->execute()->fetchAll(); + case self::FORMAT_FILE_APP: + $query = OCP\DB::prepare('SELECT id, path, name, ctime, mtime, mimetype, size, encrypted, versioned, writable FROM *PREFIX*fscache WHERE id IN ('.$ids.')'); + $result = $query->execute(); + $files = array(); + while ($file = $result->fetchRow()) { + // Set target path + $file['path'] = $items[$file['id']]['item_target']; + $file['name'] = basename($file['path']); + // TODO Set permissions: $file['writable'] + $files[] = $file; + } + return $files; + } + } + +} + +?> \ No newline at end of file diff --git a/apps/media/lib_ampache.php b/apps/media/lib_ampache.php index 8f8f1e985a..807d94bcde 100644 --- a/apps/media/lib_ampache.php +++ b/apps/media/lib_ampache.php @@ -77,7 +77,7 @@ class OC_MEDIA_AMPACHE{ $songs=OC_MEDIA_COLLECTION::getSongCount(); $artists=OC_MEDIA_COLLECTION::getArtistCount(); $albums=OC_MEDIA_COLLECTION::getAlbumCount(); - $query=OCP\DB::prepare("INSERT INTO `*PREFIX*media_sessions` (`session_id`, `token`, `user_id`, `start`) VALUES (NULL, ?, ?, now());"); + $query=OCP\DB::prepare("INSERT INTO `*PREFIX*media_sessions` (`token`, `user_id`, `start`) VALUES (?, ?, now());"); $query->execute(array($token,$user)); $expire=date('c',time()+600); echo(''); @@ -136,8 +136,14 @@ class OC_MEDIA_AMPACHE{ return false; } } + $CONFIG_DBTYPE = OCP\Config::getSystemValue( "dbtype", "sqlite" ); + if($CONFIG_DBTYPE == 'psql'){ + $interval = ' \'600s\'::interval '; + }else { + $interval = '600'; + } //remove old sessions - $query=OCP\DB::prepare("DELETE FROM `*PREFIX*media_sessions` WHERE `start`<(NOW()-600)"); + $query=OCP\DB::prepare("DELETE FROM `*PREFIX*media_sessions` WHERE `start`<(NOW() - ".$interval.")"); $query->execute(); $query=OCP\DB::prepare("SELECT `user_id` FROM `*PREFIX*media_sessions` WHERE `token`=?"); @@ -266,7 +272,6 @@ class OC_MEDIA_AMPACHE{ "); return; } - global $SITEROOT; $filter = isset($params['filter']) ? $params['filter'] : ''; $albums=OC_MEDIA_COLLECTION::getAlbums($filter); $artist=OC_MEDIA_COLLECTION::getArtistName($filter); @@ -414,5 +419,3 @@ class OC_MEDIA_AMPACHE{ echo(''); } } - -?> diff --git a/apps/media/lib_collection.php b/apps/media/lib_collection.php index b59e6f8210..c7265caaec 100644 --- a/apps/media/lib_collection.php +++ b/apps/media/lib_collection.php @@ -27,7 +27,6 @@ class OC_MEDIA_COLLECTION{ public static $uid; private static $artistIdCache=array(); private static $albumIdCache=array(); - private static $songIdCache=array(); private static $queries=array(); /** @@ -152,7 +151,7 @@ class OC_MEDIA_COLLECTION{ return $artistId; }else{ $query=OCP\DB::prepare("INSERT INTO `*PREFIX*media_artists` (`artist_name`) VALUES (?)"); - $result=$query->execute(array($name)); + $query->execute(array($name)); return self::getArtistId($name);; } } @@ -387,5 +386,3 @@ class OC_MEDIA_COLLECTION{ $query->execute(array($newPath,$oldPath)); } } - -?> diff --git a/apps/media/lib_media.php b/apps/media/lib_media.php index f9f10584dd..ff58e4e735 100644 --- a/apps/media/lib_media.php +++ b/apps/media/lib_media.php @@ -21,26 +21,14 @@ * */ -//we need to have the sha256 hash of passwords for ampache -OCP\Util::connectHook('OC_User','post_login','OC_MEDIA','loginListener'); - -//connect to the filesystem for auto updating -OCP\Util::connectHook('OC_Filesystem','post_write','OC_MEDIA','updateFile'); - -//listen for file deletions to clean the database if a song is deleted -OCP\Util::connectHook('OC_Filesystem','post_delete','OC_MEDIA','deleteFile'); - -//list for file moves to update the database -OCP\Util::connectHook('OC_Filesystem','post_rename','OC_MEDIA','moveFile'); - class OC_MEDIA{ /** * get the sha256 hash of the password needed for ampache * @param array $params, parameters passed from OC_Hook */ public static function loginListener($params){ - if(isset($_POST['user']) and $_POST['password']){ - $name=$_POST['user']; + if(isset($params['uid']) and $params['password']){ + $name=$params['uid']; $query=OCP\DB::prepare("SELECT `user_id` from `*PREFIX*media_users` WHERE `user_id` LIKE ?"); $uid=$query->execute(array($name))->fetchAll(); if(count($uid)==0){ diff --git a/apps/media/lib_scanner.php b/apps/media/lib_scanner.php index a8218c3a4d..3c32879eee 100644 --- a/apps/media/lib_scanner.php +++ b/apps/media/lib_scanner.php @@ -61,7 +61,6 @@ class OC_MEDIA_SCANNER{ * @return boolean */ public static function scanFile($path){ - $file=OC_Filesystem::getLocalFile($path); if(!self::isMusic($path)){ return; } @@ -69,6 +68,7 @@ class OC_MEDIA_SCANNER{ self::$getID3=@new getID3(); self::$getID3->encoding='UTF-8'; } + $file=OC_Filesystem::getLocalFile($path); $data=@self::$getID3->analyze($file); getid3_lib::CopyTagsToComments($data); if(!isset($data['comments'])){ diff --git a/apps/media/remote.php b/apps/media/remote.php index 01add42b31..0535077cef 100644 --- a/apps/media/remote.php +++ b/apps/media/remote.php @@ -5,7 +5,7 @@ $RUNTIME_APPTYPES=array('filesystem','authentication'); OC_App::loadApps($RUNTIME_APPTYPES); if($path_info == '/ampache' || $path_info == '/ampache/'){ - require_once(OC::$APPSROOT . '/apps/media/index.php'); + require_once(OC_App::getAppPath('media').'/index.php'); }else{ - require_once(OC::$APPSROOT . '/apps/media/server/xml.server.php'); + require_once(OC_App::getAppPath('media').'/server/xml.server.php'); } diff --git a/apps/media/server/xml.server.php b/apps/media/server/xml.server.php index 6cb6c91ca0..796da130a4 100644 --- a/apps/media/server/xml.server.php +++ b/apps/media/server/xml.server.php @@ -22,8 +22,8 @@ */ OCP\App::checkAppEnabled('media'); - require_once(OC::$APPSROOT . '/apps/media/lib_collection.php'); - require_once(OC::$APPSROOT . '/apps/media/lib_ampache.php'); + require_once(OC_App::getAppPath('media').'/lib_collection.php'); + require_once(OC_App::getAppPath('media').'/lib_ampache.php'); $arguments=$_POST; if(!isset($_POST['action']) and isset($_GET['action'])){ diff --git a/apps/media/settings.php b/apps/media/settings.php index 227298fafe..53738f02f9 100644 --- a/apps/media/settings.php +++ b/apps/media/settings.php @@ -3,4 +3,3 @@ $tmpl = new OCP\Template( 'media', 'settings'); return $tmpl->fetchPage(); -?> diff --git a/apps/remoteStorage/ajax/revokeToken.php b/apps/remoteStorage/ajax/revokeToken.php index 699b9e9aee..e6a6818994 100644 --- a/apps/remoteStorage/ajax/revokeToken.php +++ b/apps/remoteStorage/ajax/revokeToken.php @@ -5,7 +5,7 @@ * * Original: * @author Frank Karlitschek -* @copyright 2010 Frank Karlitschek karlitschek@kde.org +* @copyright 2012 Frank Karlitschek frank@owncloud.org * * Adapted: * @author Michiel de Jong, 2012 @@ -25,10 +25,6 @@ * */ - -// Do not load FS ... -$RUNTIME_NOSETUPFS = true; - OCP\App::checkAppEnabled('remoteStorage'); require_once('remoteStorage/lib_remoteStorage.php'); diff --git a/apps/remoteStorage/appinfo/app.php b/apps/remoteStorage/appinfo/app.php index 14b8a3d11d..c278fd7305 100644 --- a/apps/remoteStorage/appinfo/app.php +++ b/apps/remoteStorage/appinfo/app.php @@ -1,6 +1,2 @@ 10, - 'id' => 'remoteStorage', - 'name' => 'remoteStorage compatibility' )); OCP\App::registerPersonal('remoteStorage','settings'); diff --git a/apps/remoteStorage/appinfo/info.xml b/apps/remoteStorage/appinfo/info.xml index fa878762a0..1388ad9c31 100644 --- a/apps/remoteStorage/appinfo/info.xml +++ b/apps/remoteStorage/appinfo/info.xml @@ -7,4 +7,7 @@ Michiel de Jong 4 true + + webdav.php + diff --git a/apps/remoteStorage/appinfo/version b/apps/remoteStorage/appinfo/version index 490f510fc2..0e2c93950b 100644 --- a/apps/remoteStorage/appinfo/version +++ b/apps/remoteStorage/appinfo/version @@ -1 +1 @@ -0.6 \ No newline at end of file +0.7 \ No newline at end of file diff --git a/apps/remoteStorage/appinfo/webfinger.php b/apps/remoteStorage/appinfo/webfinger.php index 5d481f315f..e8b237628c 100644 --- a/apps/remoteStorage/appinfo/webfinger.php +++ b/apps/remoteStorage/appinfo/webfinger.php @@ -1,8 +1,8 @@ - + { "rel":"remoteStorage", - "template":"/apps/remoteStorage/WebDAV.php//remoteStorage/{category}/", + "template":"/remote.php/remoteStorage//remoteStorage/{category}/", "api":"WebDAV", "auth":"/?app=remoteStorage&getfile=auth.php&userid=" } - + diff --git a/apps/remoteStorage/auth.php b/apps/remoteStorage/auth.php index a54be37b2e..91ca43ea07 100644 --- a/apps/remoteStorage/auth.php +++ b/apps/remoteStorage/auth.php @@ -5,7 +5,7 @@ * * Original: * @author Frank Karlitschek -* @copyright 2010 Frank Karlitschek karlitschek@kde.org +* @copyright 2012 Frank Karlitschek frank@owncloud.org * * Adapted: * @author Michiel de Jong, 2012 @@ -25,9 +25,7 @@ * */ - -// Do not load FS ... -$RUNTIME_NOSETUPFS = true; +header("X-Frame-Options: Sameorigin"); OCP\App::checkAppEnabled('remoteStorage'); require_once('Sabre/autoload.php'); @@ -43,9 +41,9 @@ foreach($_GET as $k => $v) { $userId=$v; } else if($k=='redirect_uri'){ $appUrlParts=explode('/', $v); - $appUrl = $appUrlParts[2];//bit dodgy i guess + $appUrl = htmlentities($appUrlParts[2]);//TODO: check if this is equal to client_id } else if($k=='scope'){ - $categories=$v; + $categories=htmlentities($v); } } $currUser = OCP\USER::getUser(); @@ -59,64 +57,23 @@ if($userId && $appUrl && $categories) { header('Location: '.$_GET['redirect_uri'].'#access_token='.$existingToken.'&token_type=bearer'); } else { //params ok, logged in ok, but need to click Allow still: -?> - - - - ownCloud - - - - - - -
          -
          - -
          -
          -
          -

          remoteStorage

          -

          - requests read & write access to your - '.$categories[0].''; - if(count($categories)==2) { - echo ' and '.$categories[1].''; - } else if(count($categories)>2) { - for($i=1; $i'.$categories[$i].''; - } - echo ', and '.$categories[$i].''; - } - } - ?>. -

          -
          - - -
          -
          -
          -
          -

          ownCloud – web services under your control

          - - - $host, + 'categories' => $categories, + )); + }//end 'need to click Allow still' } else {//login not ok if($currUser) { - die('You are logged in as '.$currUser.' instead of '.$userId); + die('You are logged in as '.$currUser.' instead of '.htmlentities($userId)); } else { - header('Location: /?redirect_url='.urlencode('/apps/remoteStorage/auth.php'.$_SERVER['PATH_INFO'].'?'.$_SERVER['QUERY_STRING'])); + // this will display the login page for us + OCP\Util::checkLoggedIn(); } } } else {//params not ok - die('please use e.g. /?app=remoteStorage&getfile=auth.php&userid=admin&redirect_uri=http://host/path&scope=...'); + die('please use e.g. '.OCP\Util::linkTo('remoteStorage', 'auth.php').'?userid=admin&redirect_uri=http://host/path&scope=...'); } diff --git a/apps/remoteStorage/img/remoteStorage.png b/apps/remoteStorage/img/remoteStorage.png new file mode 100644 index 0000000000..10c2be243c Binary files /dev/null and b/apps/remoteStorage/img/remoteStorage.png differ diff --git a/apps/remoteStorage/lib_remoteStorage.php b/apps/remoteStorage/lib_remoteStorage.php index b6934e87d6..b9e2368003 100644 --- a/apps/remoteStorage/lib_remoteStorage.php +++ b/apps/remoteStorage/lib_remoteStorage.php @@ -2,8 +2,8 @@ class OC_remoteStorage { public static function getValidTokens($ownCloudUser, $category) { - $query=OCP\DB::prepare("SELECT `token`,`appUrl`,`category` FROM `*PREFIX*authtoken` WHERE `user`=?",100); - $result=$query->execute(array($ownCloudUser)); + $stmt=OCP\DB::prepare("SELECT `token`,`appUrl`,`category` FROM `*PREFIX*authtoken` WHERE `user`=?",100); + $result=$stmt->execute(array($ownCloudUser)); $ret = array(); while($row=$result->fetchRow()){ if(in_array($category, explode(',', $row['category']))) { @@ -15,20 +15,19 @@ class OC_remoteStorage { public static function getTokenFor($appUrl, $categories) { $user=OCP\USER::getUser(); - $query=OCP\DB::prepare("SELECT `token` FROM `*PREFIX*authtoken` WHERE `user`=? AND `appUrl`=? AND `category`=?",1); - $result=$query->execute(array($user, $appUrl, $categories)); - $ret = array(); + $stmt=OCP\DB::prepare("SELECT `token` FROM `*PREFIX*authtoken` WHERE `user`=? AND `appUrl`=? AND `category`=?",1); + $result=$stmt->execute(array($user, $appUrl, $categories)); if($row=$result->fetchRow()) { - return base64_encode('remoteStorage:'.$row['token']); - } else { - return false; - } + return base64_encode('remoteStorage:'.$row['token']); + } else { + return false; + } } public static function getAllTokens() { $user=OCP\USER::getUser(); - $query=OCP\DB::prepare("SELECT `token`,`appUrl`,`category` FROM `*PREFIX*authtoken` WHERE `user`=?",100); - $result=$query->execute(array($user)); + $stmt=OCP\DB::prepare("SELECT `token`,`appUrl`,`category` FROM `*PREFIX*authtoken` WHERE `user`=?",100); + $result=$stmt->execute(array($user)); $ret = array(); while($row=$result->fetchRow()){ $ret[$row['token']] = array( @@ -41,14 +40,14 @@ class OC_remoteStorage { public static function deleteToken($token) { $user=OCP\USER::getUser(); - $query=OCP\DB::prepare("DELETE FROM `*PREFIX*authtoken` WHERE `token`=? AND `user`=?"); - $result=$query->execute(array($token,$user)); + $stmt=OCP\DB::prepare("DELETE FROM `*PREFIX*authtoken` WHERE `token`=? AND `user`=?"); + $stmt->execute(array($token,$user)); return 'unknown';//how can we see if any rows were affected? } private static function addToken($token, $appUrl, $categories){ $user=OCP\USER::getUser(); - $query=OCP\DB::prepare("INSERT INTO `*PREFIX*authtoken` (`token`,`appUrl`,`user`,`category`) VALUES(?,?,?,?)"); - $result=$query->execute(array($token,$appUrl,$user,$categories)); + $stmt=OCP\DB::prepare("INSERT INTO `*PREFIX*authtoken` (`token`,`appUrl`,`user`,`category`) VALUES(?,?,?,?)"); + $stmt->execute(array($token,$appUrl,$user,$categories)); } public static function createCategories($appUrl, $categories) { $token=uniqid(); diff --git a/apps/remoteStorage/oauth_ro_auth.php b/apps/remoteStorage/oauth_ro_auth.php index 12d02d1cf5..bed3093c3b 100644 --- a/apps/remoteStorage/oauth_ro_auth.php +++ b/apps/remoteStorage/oauth_ro_auth.php @@ -9,10 +9,10 @@ class OC_Connector_Sabre_Auth_ro_oauth extends Sabre_DAV_Auth_Backend_AbstractBasic { private $validTokens; - private $category; + private $category; public function __construct($validTokensArg, $categoryArg) { $this->validTokens = $validTokensArg; - $this->category = $categoryArg; + $this->category = $categoryArg; } /** @@ -25,16 +25,16 @@ class OC_Connector_Sabre_Auth_ro_oauth extends Sabre_DAV_Auth_Backend_AbstractBa */ protected function validateUserPass($username, $password){ //always give read-only: - if(($_SERVER['REQUEST_METHOD'] == 'OPTIONS') + if(($_SERVER['REQUEST_METHOD'] == 'OPTIONS') || (isset($this->validTokens[$password])) - || (($_SERVER['REQUEST_METHOD'] == 'GET') && ($this->category == 'public')) - ) { + || (($_SERVER['REQUEST_METHOD'] == 'GET') && ($this->category == 'public')) + ) { OC_Util::setUpFS(); return true; } else { - //var_export($_SERVER); - //var_export($this->validTokens); - //die('not getting in with "'.$username.'"/"'.$password.'"!'); + //var_export($_SERVER); + //var_export($this->validTokens); + //die('not getting in with "'.$username.'"/"'.$password.'"!'); return false; } } @@ -48,8 +48,8 @@ class OC_Connector_Sabre_Auth_ro_oauth extends Sabre_DAV_Auth_Backend_AbstractBa $userpass = $auth->getUserPass(); if (!$userpass) { if(($_SERVER['REQUEST_METHOD'] == 'OPTIONS') - ||(($_SERVER['REQUEST_METHOD'] == 'GET') && ($this->category == 'public')) - ) { + ||(($_SERVER['REQUEST_METHOD'] == 'GET') && ($this->category == 'public')) + ) { $userpass = array('', ''); } else { $auth->requireLogin(); diff --git a/apps/remoteStorage/remoteStorage.png b/apps/remoteStorage/remoteStorage.png deleted file mode 100644 index 6b751c0999..0000000000 Binary files a/apps/remoteStorage/remoteStorage.png and /dev/null differ diff --git a/apps/remoteStorage/settings.php b/apps/remoteStorage/settings.php index 9c48549fe6..3be8b0984d 100644 --- a/apps/remoteStorage/settings.php +++ b/apps/remoteStorage/settings.php @@ -4,4 +4,3 @@ require_once('lib_remoteStorage.php'); $tmpl = new OCP\Template( 'remoteStorage', 'settings'); return $tmpl->fetchPage(); -?> diff --git a/apps/remoteStorage/templates/auth.php b/apps/remoteStorage/templates/auth.php new file mode 100644 index 0000000000..6a7054eabb --- /dev/null +++ b/apps/remoteStorage/templates/auth.php @@ -0,0 +1,28 @@ +
          +
          +

          remoteStorage

          +

          + requests read & write access to your + '.$categories[0].''; + if(count($categories)==2) { + echo ' and '.$categories[1].''; + } else if(count($categories)>2) { + for($i=1; $i'.$categories[$i].''; + } + echo ', and '.$categories[$i].''; + } + } + ?>. +

          +
          + + +
          +
          +
          diff --git a/apps/remoteStorage/templates/settings.php b/apps/remoteStorage/templates/settings.php index 147378dda3..1d2a188f52 100644 --- a/apps/remoteStorage/templates/settings.php +++ b/apps/remoteStorage/templates/settings.php @@ -1,10 +1,6 @@
          - ' - .''.$l->t('remoteStorage').' user address: ' - .OCP\USER::getUser().'@'.$_SERVER['SERVER_NAME'] - .' (more info)'; - ?> + + t('remoteStorage') ?> user address: (more info)

          Apps that currently have access to your ownCloud:

            - $details) { - echo '
          • '.$details['appUrl'].': '.$details['categories'] - .'
          • '."\n"; - } - ?>
          + $details) { ?> +
        • + : + +
        • + +

        diff --git a/apps/remoteStorage/WebDAV.php b/apps/remoteStorage/webdav.php similarity index 70% rename from apps/remoteStorage/WebDAV.php rename to apps/remoteStorage/webdav.php index ab498db07d..8d8ec6a45a 100644 --- a/apps/remoteStorage/WebDAV.php +++ b/apps/remoteStorage/webdav.php @@ -5,7 +5,7 @@ * * Original: * @author Frank Karlitschek -* @copyright 2010 Frank Karlitschek karlitschek@kde.org +* @copyright 2012 Frank Karlitschek frank@owncloud.org * * Adapted: * @author Michiel de Jong, 2011 @@ -25,22 +25,7 @@ * */ - -// Do not load FS ... -$RUNTIME_NOSETUPFS = true; - - -require_once('../../lib/base.php'); - -require_once('../../lib/user.php'); -require_once('../../lib/public/user.php'); - -require_once('../../lib/app.php'); -require_once('../../lib/public/app.php'); - -require_once('../../3rdparty/Sabre/DAV/Auth/IBackend.php'); -require_once('../../3rdparty/Sabre/DAV/Auth/Backend/AbstractBasic.php'); -require_once('../../lib/connector/sabre/auth.php'); +OC_App::loadApps(array('filesystem','authentication')); OCP\App::checkAppEnabled('remoteStorage'); require_once('lib_remoteStorage.php'); @@ -61,14 +46,15 @@ if(isset($_SERVER['HTTP_ORIGIN'])) { header('Access-Control-Allow-Origin: *'); } -$path = substr($_SERVER["REQUEST_URI"], strlen($_SERVER["SCRIPT_NAME"])); +$path = substr($_SERVER["REQUEST_URI"], strlen($baseuri)); + $pathParts = explode('/', $path); // for webdav: -// 0/ 1 / 2 / 3... -// /$ownCloudUser/remoteStorage/$category/ +// 0 / 1 / 2... +// $ownCloudUser/remoteStorage/$category/ -if(count($pathParts) >= 3 && $pathParts[0] == '') { - list($dummy, $ownCloudUser, $dummy2, $category) = $pathParts; +if(count($pathParts) >= 2) { + list($ownCloudUser, $dummy2, $category) = $pathParts; OC_Util::setupFS($ownCloudUser); @@ -77,13 +63,13 @@ if(count($pathParts) >= 3 && $pathParts[0] == '') { $server = new Sabre_DAV_Server($publicDir); // Path to our script - $server->setBaseUri(OC::$WEBROOT."/apps/remoteStorage/WebDAV.php/$ownCloudUser"); + $server->setBaseUri($baseuri.$ownCloudUser); // Auth backend $authBackend = new OC_Connector_Sabre_Auth_ro_oauth( - OC_remoteStorage::getValidTokens($ownCloudUser, $category), - $category - ); + OC_remoteStorage::getValidTokens($ownCloudUser, $category), + $category + ); $authPlugin = new Sabre_DAV_Auth_Plugin($authBackend,'ownCloud');//should use $validTokens here $server->addPlugin($authPlugin); diff --git a/apps/tasks/ajax/addtask.php b/apps/tasks/ajax/addtask.php index 188e179236..d98fdbf388 100644 --- a/apps/tasks/ajax/addtask.php +++ b/apps/tasks/ajax/addtask.php @@ -22,7 +22,7 @@ $request['description'] = null; $vcalendar = OC_Task_App::createVCalendarFromRequest($request); $id = OC_Calendar_Object::add($cid, $vcalendar->serialize()); -$user_timezone = OCP\Config::getUserValue(OCP\User::getUser(), 'calendar', 'timezone', date_default_timezone_get()); +$user_timezone = OC_Calendar_App::getTimezone(); $task = OC_Task_App::arrayForJSON($id, $vcalendar->VTODO, $user_timezone); OCP\JSON::success(array('task' => $task)); diff --git a/apps/tasks/ajax/addtaskform.php b/apps/tasks/ajax/addtaskform.php deleted file mode 100644 index 2795f39373..0000000000 --- a/apps/tasks/ajax/addtaskform.php +++ /dev/null @@ -1,21 +0,0 @@ -assign('calendars',$calendars); -$tmpl->assign('category_options', $category_options); -$tmpl->assign('percent_options', $percent_options); -$tmpl->assign('priority_options', $priority_options); -$tmpl->assign('details', new OC_VObject('VTODO')); -$tmpl->assign('categories', ''); -$page = $tmpl->fetchPage(); - -OCP\JSON::success(array('data' => array( 'page' => $page ))); diff --git a/apps/tasks/ajax/edittask.php b/apps/tasks/ajax/edittask.php deleted file mode 100644 index 77ecff13e6..0000000000 --- a/apps/tasks/ajax/edittask.php +++ /dev/null @@ -1,32 +0,0 @@ - array( 'errors' => $errors ))); - exit(); -} - -OC_Task_App::updateVCalendarFromRequest($_POST, $vcalendar); -OC_Calendar_Object::edit($id, $vcalendar->serialize()); - -$priority_options = OC_Task_App::getPriorityOptions(); -$tmpl = new OCP\Template('tasks','part.details'); -$tmpl->assign('priority_options', $priority_options); -$tmpl->assign('details', $vcalendar->VTODO); -$tmpl->assign('id', $id); -$page = $tmpl->fetchPage(); - -$user_timezone = OCP\Config::getUserValue(OCP\User::getUser(), 'calendar', 'timezone', date_default_timezone_get()); -$task = OC_Task_App::arrayForJSON($id, $vcalendar->VTODO, $user_timezone); - -OCP\JSON::success(array('data' => array( 'id' => $id, 'page' => $page, 'task' => $task ))); diff --git a/apps/tasks/ajax/edittaskform.php b/apps/tasks/ajax/edittaskform.php deleted file mode 100644 index e5a0a7297c..0000000000 --- a/apps/tasks/ajax/edittaskform.php +++ /dev/null @@ -1,24 +0,0 @@ -VTODO; -$categories = $details->getAsString('CATEGORIES'); - -$category_options = OC_Calendar_App::getCategoryOptions(); -$percent_options = range(0, 100, 10); -$priority_options = OC_Task_App::getPriorityOptions(); - -$tmpl = new OCP\Template('tasks','part.edittaskform'); -$tmpl->assign('category_options', $category_options); -$tmpl->assign('percent_options', $percent_options); -$tmpl->assign('priority_options', $priority_options); -$tmpl->assign('id',$id); -$tmpl->assign('details',$details); -$tmpl->assign('categories', $categories); -$page = $tmpl->fetchPage(); - -OCP\JSON::success(array('data' => array( 'page' => $page ))); diff --git a/apps/tasks/ajax/getdetails.php b/apps/tasks/ajax/getdetails.php deleted file mode 100644 index 4ce469e0c9..0000000000 --- a/apps/tasks/ajax/getdetails.php +++ /dev/null @@ -1,24 +0,0 @@ -assign('priority_options', $priority_options); -$tmpl->assign('details',$details->VTODO); -$tmpl->assign('id',$id); -$page = $tmpl->fetchPage(); - -OCP\JSON::success(array('data' => array( 'id' => $id, 'page' => $page ))); diff --git a/apps/tasks/ajax/gettasks.php b/apps/tasks/ajax/gettasks.php index 011730d0a1..b6183d9cb6 100644 --- a/apps/tasks/ajax/gettasks.php +++ b/apps/tasks/ajax/gettasks.php @@ -11,7 +11,7 @@ OCP\JSON::checkLoggedIn(); OCP\JSON::checkAppEnabled('tasks'); $calendars = OC_Calendar_Calendar::allCalendars(OCP\User::getUser(), true); -$user_timezone = OCP\Config::getUserValue(OCP\User::getUser(), 'calendar', 'timezone', date_default_timezone_get()); +$user_timezone = OC_Calendar_App::getTimezone(); $tasks = array(); foreach( $calendars as $calendar ){ diff --git a/apps/tasks/ajax/update_property.php b/apps/tasks/ajax/update_property.php index 46521cf6c5..679cfdefe4 100644 --- a/apps/tasks/ajax/update_property.php +++ b/apps/tasks/ajax/update_property.php @@ -9,6 +9,7 @@ // Init owncloud OCP\JSON::checkLoggedIn(); OCP\JSON::checkAppEnabled('tasks'); +OCP\JSON::callCheck(); $id = $_POST['id']; $property = $_POST['type']; @@ -38,7 +39,7 @@ switch($property) { $type = null; if ($due != 'false') { try { - $timezone = OCP\Config::getUserValue(OCP\User::getUser(), 'calendar', 'timezone', date_default_timezone_get()); + $timezone = OC_Calendar_App::getTimezone(); $timezone = new DateTimeZone($timezone); $due = new DateTime('@'.$due); $due->setTimezone($timezone); @@ -63,6 +64,6 @@ switch($property) { } OC_Calendar_Object::edit($id, $vcalendar->serialize()); -$user_timezone = OCP\Config::getUserValue(OCP\User::getUser(), 'calendar', 'timezone', date_default_timezone_get()); +$user_timezone = OC_Calendar_App::getTimezone(); $task_info = OC_Task_App::arrayForJSON($id, $vtodo, $user_timezone); OCP\JSON::success(array('data' => $task_info)); diff --git a/apps/tasks/appinfo/app.php b/apps/tasks/appinfo/app.php index f346e2aa4c..e7c82d6f24 100644 --- a/apps/tasks/appinfo/app.php +++ b/apps/tasks/appinfo/app.php @@ -3,11 +3,6 @@ $l=new OC_L10N('tasks'); OC::$CLASSPATH['OC_Calendar_Calendar'] = 'apps/calendar/lib/calendar.php'; OC::$CLASSPATH['OC_Task_App'] = 'apps/tasks/lib/app.php'; -OCP\App::register( array( - 'order' => 11, - 'id' => 'tasks', - 'name' => 'Tasks' )); - OCP\App::addNavigationEntry( array( 'id' => 'tasks_index', 'order' => 11, diff --git a/apps/tasks/css/style.css b/apps/tasks/css/style.css index f6333f57b8..80b6e777b5 100644 --- a/apps/tasks/css/style.css +++ b/apps/tasks/css/style.css @@ -9,7 +9,7 @@ #tasks_lists .active{font-weight:bold;} #tasks_list h1{background-color:#1D2D44;color:white;font-size:120%;padding:0 0.5em;} -.task{border-radius:0.4em;position:relative;padding:0.5em 1em;} +.task{border-radius:0.4em;position:relative;padding:0.4em 1em;} .task:nth-child(odd){background-color:#F4F4F4;} .task:hover {background-color:#DDDDDD;} @@ -42,18 +42,19 @@ .task:hover .tag{opacity:0.5} .task:hover .tag:hover{opacity:0.8;} -.task .categories{position:absolute;right:12em;text-align:right;top:0.4em} +.task .categories{position:absolute;right:12em;text-align:right;top:0.5em} .task .categories a{background-color:#1d2d44;color:white;} .task .categories .tag.active{display:none;} .task input.categories{display:none;top:0;text-align:left;} -.task .location{background-color:#442d44;color:white;position:absolute;right:0.6em;width:9.2em;text-align:left;top:0.4em} +.task .location{background-color:#442d44;color:white;position:absolute;right:0.6em;width:9.2em;text-align:left;top:0.5em} .task input.location{display:none;top:0;text-align:left;right:0.3em;background-color:white;color:#333333;} .task .more{display:none;margin-top:0.5em;} .task_less{display:none;} .task .description{position:relative;left:4em;} +.task textarea.description{width:35em;height:4em;} .task .due{position:absolute;right:0.3em;} .task .due .date{width:6em;} .task .due .time{width:6em;} diff --git a/apps/tasks/img/icon.png b/apps/tasks/img/icon.png index df281f3ba8..e2802ae938 100644 Binary files a/apps/tasks/img/icon.png and b/apps/tasks/img/icon.png differ diff --git a/apps/tasks/index.php b/apps/tasks/index.php index 5e17ca454e..f1c4d1e765 100644 --- a/apps/tasks/index.php +++ b/apps/tasks/index.php @@ -21,12 +21,11 @@ OCP\Util::addScript('3rdparty/timepicker', 'jquery.ui.timepicker'); OCP\Util::addStyle('3rdparty/timepicker', 'jquery.ui.timepicker'); OCP\Util::addScript('tasks', 'tasks'); OCP\Util::addStyle('tasks', 'style'); -OCP\Util::addScript('contacts','jquery.multi-autocomplete'); -OCP\Util::addScript('','oc-vcategories'); +OCP\Util::addScript('contacts', 'jquery.multi-autocomplete'); +OCP\Util::addScript('', 'oc-vcategories'); OCP\App::setActiveNavigationEntry('tasks_index'); $categories = OC_Calendar_App::getCategoryOptions(); -$l10n = new OC_L10N('tasks'); $priority_options = OC_Task_App::getPriorityOptions(); $output = new OCP\Template('tasks', 'tasks', 'user'); $output->assign('priority_options', $priority_options); diff --git a/apps/tasks/js/tasks.js b/apps/tasks/js/tasks.js index bc92965bb0..de62792750 100644 --- a/apps/tasks/js/tasks.js +++ b/apps/tasks/js/tasks.js @@ -469,67 +469,5 @@ $(document).ready(function(){ return false; }); - $('#tasks_addtaskform input[type="submit"]').live('click',function(){ - $.post('ajax/addtask.php',$('#tasks_addtaskform').serialize(),function(jsondata){ - if(jsondata.status == 'success'){ - $('#task_details').data('id',jsondata.data.id); - $('#task_details').html(jsondata.data.page); - $('#tasks_list').append(OC.Tasks.create_task_div(jsondata.data.task)); - } - else{ - alert(jsondata.data.message); - } - }, 'json'); - return false; - }); - - $('#tasks_edit').live('click',function(){ - var id = $('#task_details').data('id'); - $.getJSON('ajax/edittaskform.php',{'id':id},function(jsondata){ - if(jsondata.status == 'success'){ - $('#task_details').html(jsondata.data.page); - $('#task_details #categories').multiple_autocomplete({source: categories}); - } - else{ - alert(jsondata.data.message); - } - }); - return false; - }); - - $('#tasks_edittaskform #percent_complete').live('change',function(event){ - if ($(event.target).val() == 100){ - $('#tasks_edittaskform #complete').show(); - }else{ - $('#tasks_edittaskform #complete').hide(); - } - }); - - $('#tasks_edittaskform input[type="submit"]').live('click',function(){ - $.post('ajax/edittask.php',$('#tasks_edittaskform').serialize(),function(jsondata){ - $('.error_msg').remove(); - $('.error').removeClass('error'); - if(jsondata.status == 'success'){ - var id = jsondata.data.id; - $('#task_details').data('id',id); - $('#task_details').html(jsondata.data.page); - var task = jsondata.data.task; - $('#tasks .task[data-id='+id+']') - .data('task', task) - .html(OC.Tasks.create_task_div(task).html()); - } - else{ - var errors = jsondata.data.errors; - for (k in errors){ - $('#'+k).addClass('error') - .after(''+errors[k]+''); - } - $('.error_msg').effect('highlight', {}, 3000); - $('.error').effect('highlight', {}, 3000); - } - }, 'json'); - return false; - }); - OCCategories.app = 'calendar'; }); diff --git a/apps/tasks/l10n/.gitkeep b/apps/tasks/l10n/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apps/tasks/l10n/ca.php b/apps/tasks/l10n/ca.php new file mode 100644 index 0000000000..2608d8b9b1 --- /dev/null +++ b/apps/tasks/l10n/ca.php @@ -0,0 +1,24 @@ + "data/hora incorrecta", +"Tasks" => "Tasques", +"No category" => "Cap categoria", +"Unspecified" => "Sense especificar", +"1=highest" => "1=major", +"5=medium" => "5=mitjana", +"9=lowest" => "9=inferior", +"Empty Summary" => "Elimina el resum", +"Invalid percent complete" => "Percentatge completat no vàlid", +"Invalid priority" => "Prioritat no vàlida", +"Add Task" => "Afegeix una tasca", +"Order Due" => "Ordena per", +"Order List" => "Ordena per llista", +"Order Complete" => "Ordena els complets", +"Order Location" => "Ordena per ubicació", +"Order Priority" => "Ordena per prioritat", +"Order Label" => "Ordena per etiqueta", +"Loading tasks..." => "Carregant les tasques...", +"Important" => "Important", +"More" => "Més", +"Less" => "Menys", +"Delete" => "Elimina" +); diff --git a/apps/tasks/l10n/cs_CZ.php b/apps/tasks/l10n/cs_CZ.php new file mode 100644 index 0000000000..9da5f7ab60 --- /dev/null +++ b/apps/tasks/l10n/cs_CZ.php @@ -0,0 +1,15 @@ + "Neplatné datum/čas", +"Tasks" => "Úkoly", +"No category" => "Bez kategorie", +"1=highest" => "1=nejvyšší", +"5=medium" => "5=střední", +"9=lowest" => "9=nejnižší", +"Invalid priority" => "Neplatná priorita", +"Add Task" => "Přidat úkol", +"Loading tasks..." => "Načítám úkoly...", +"Important" => "Důležité", +"More" => "Více", +"Less" => "Méně", +"Delete" => "Smazat" +); diff --git a/apps/tasks/l10n/da.php b/apps/tasks/l10n/da.php new file mode 100644 index 0000000000..a413f22d17 --- /dev/null +++ b/apps/tasks/l10n/da.php @@ -0,0 +1,16 @@ + "Ugyldig dato/tid", +"Tasks" => "Opgaver", +"No category" => "Ingen kategori", +"Unspecified" => "Uspecificeret", +"1=highest" => "1=højeste", +"5=medium" => "5=mellem", +"9=lowest" => "9=laveste", +"Empty Summary" => "Tom beskrivelse", +"Add Task" => "Tilføj opgave", +"Loading tasks..." => "Indlæser opgaver...", +"Important" => "vigtigt", +"More" => "Mere", +"Less" => "Mindre", +"Delete" => "Slet" +); diff --git a/apps/tasks/l10n/de.php b/apps/tasks/l10n/de.php new file mode 100644 index 0000000000..29b9bf41ec --- /dev/null +++ b/apps/tasks/l10n/de.php @@ -0,0 +1,24 @@ + "Datum/Uhrzeit ungültig", +"Tasks" => "Aufgaben", +"No category" => "Keine Kategorie", +"Unspecified" => "Nicht angegeben", +"1=highest" => "1 = am höchsten", +"5=medium" => "5 = Durchschnitt", +"9=lowest" => "9 = am niedrigsten", +"Empty Summary" => "Leere Zusammenfassung", +"Invalid percent complete" => "Ungültige Prozent abgeschlossen", +"Invalid priority" => "Falsche Priorität", +"Add Task" => "Aufgabe hinzufügen", +"Order Due" => "Nach Fälligkeit sortieren", +"Order List" => "Nach Kategorie sortieren ", +"Order Complete" => "Nach Fertigstellung sortieren", +"Order Location" => "Nach Ort sortieren", +"Order Priority" => "Nach Priorität sortieren", +"Order Label" => "Nach Label sortieren", +"Loading tasks..." => "Lade Aufgaben ...", +"Important" => "Wichtig", +"More" => "Mehr", +"Less" => "Weniger", +"Delete" => "Löschen" +); diff --git a/apps/tasks/l10n/el.php b/apps/tasks/l10n/el.php new file mode 100644 index 0000000000..cdf2f609e5 --- /dev/null +++ b/apps/tasks/l10n/el.php @@ -0,0 +1,18 @@ + "Μην έγκυρη ημερομηνία / ώρα", +"Tasks" => "Εργασίες", +"No category" => "Χωρίς κατηγορία", +"Unspecified" => "Μη ορισμένο", +"1=highest" => "1=υψηλότερο", +"5=medium" => "5=μέτριο", +"9=lowest" => "9=χαμηλότερο", +"Empty Summary" => "Άδεια περίληψη", +"Invalid percent complete" => "Μη έγκυρο ποσοστό ολοκλήρωσης", +"Invalid priority" => "Μη έγκυρη προτεραιότητα ", +"Add Task" => "Προσθήκη εργασίας", +"Loading tasks..." => "Φόρτωση εργασιών...", +"Important" => "Σημαντικό ", +"More" => "Περισσότερα", +"Less" => "Λιγότερα", +"Delete" => "Διαγραφή" +); diff --git a/apps/tasks/l10n/eo.php b/apps/tasks/l10n/eo.php new file mode 100644 index 0000000000..9919a6ea63 --- /dev/null +++ b/apps/tasks/l10n/eo.php @@ -0,0 +1,24 @@ + "Nevalida dato/horo", +"Tasks" => "Taskoj", +"No category" => "Neniu kategorio", +"Unspecified" => "Nespecifita", +"1=highest" => "1=plej alta", +"5=medium" => "5=meza", +"9=lowest" => "9=plej malalta", +"Empty Summary" => "Malplena resumo", +"Invalid percent complete" => "Nevalida plenuma elcento", +"Invalid priority" => "Nevalida pligravo", +"Add Task" => "Aldoni taskon", +"Order Due" => "Ordigi laŭ limdato", +"Order List" => "Ordigi laŭ listo", +"Order Complete" => "Ordigi laŭ plenumo", +"Order Location" => "Ordigi laŭ loko", +"Order Priority" => "Ordigi laŭ pligravo", +"Order Label" => "Ordigi laŭ etikedo", +"Loading tasks..." => "Ŝargante taskojn...", +"Important" => "Grava", +"More" => "Pli", +"Less" => "Malpli", +"Delete" => "Forigi" +); diff --git a/apps/tasks/l10n/es.php b/apps/tasks/l10n/es.php new file mode 100644 index 0000000000..e5f0981de5 --- /dev/null +++ b/apps/tasks/l10n/es.php @@ -0,0 +1,24 @@ + "Fecha/hora inválida", +"Tasks" => "Tareas", +"No category" => "Sin categoría", +"Unspecified" => "Sin especificar", +"1=highest" => "1=mayor", +"5=medium" => "5=media", +"9=lowest" => "9=menor", +"Empty Summary" => "Resumen vacío", +"Invalid percent complete" => "Porcentaje completado inválido", +"Invalid priority" => "Prioridad inválida", +"Add Task" => "Agregar tarea", +"Order Due" => "Ordenar por", +"Order List" => "Ordenar por lista", +"Order Complete" => "Ordenar por completadas", +"Order Location" => "Ordenar por ubicación", +"Order Priority" => "Ordenar por prioridad", +"Order Label" => "Ordenar por etiqueta", +"Loading tasks..." => "Cargando tareas...", +"Important" => "Importante", +"More" => "Más", +"Less" => "Menos", +"Delete" => "Borrar" +); diff --git a/apps/tasks/l10n/et_EE.php b/apps/tasks/l10n/et_EE.php new file mode 100644 index 0000000000..56db6ee032 --- /dev/null +++ b/apps/tasks/l10n/et_EE.php @@ -0,0 +1,24 @@ + "Vigane kuupäev/kellaaeg", +"Tasks" => "Ülesanded", +"No category" => "Kategooriat pole", +"Unspecified" => "Määramata", +"1=highest" => "1=kõrgeim", +"5=medium" => "5=keskmine", +"9=lowest" => "9=madalaim", +"Empty Summary" => "Tühi kokkuvõte", +"Invalid percent complete" => "Vigane edenemise protsent", +"Invalid priority" => "Vigane tähtsus", +"Add Task" => "Lisa ülesanne", +"Order Due" => "Tähtaja järgi", +"Order List" => "Nimekirja järgi", +"Order Complete" => "Edenemise järgi", +"Order Location" => "Asukoha järgi", +"Order Priority" => "Tähtsuse järjekorras", +"Order Label" => "Sildi järgi", +"Loading tasks..." => "Ülesannete laadimine...", +"Important" => "Tähtis", +"More" => "Rohkem", +"Less" => "Vähem", +"Delete" => "Kustuta" +); diff --git a/apps/tasks/l10n/fa.php b/apps/tasks/l10n/fa.php new file mode 100644 index 0000000000..40b5b610f4 --- /dev/null +++ b/apps/tasks/l10n/fa.php @@ -0,0 +1,11 @@ + "وظایف", +"1=highest" => "1=بیش‌ترین", +"5=medium" => "5=متوسط", +"9=lowest" => "9=کم‌ترین", +"Loading tasks..." => "درحال بارگزاری وظایف", +"Important" => "مهم", +"More" => "بیش‌تر", +"Less" => "کم‌تر", +"Delete" => "حذف" +); diff --git a/apps/tasks/l10n/fi_FI.php b/apps/tasks/l10n/fi_FI.php new file mode 100644 index 0000000000..7948e712e9 --- /dev/null +++ b/apps/tasks/l10n/fi_FI.php @@ -0,0 +1,17 @@ + "Virheellinen päivä tai aika", +"Tasks" => "Tehtävät", +"No category" => "Ei luokkaa", +"Unspecified" => "Määrittelemätön", +"1=highest" => "1=korkein", +"5=medium" => "5=keskitaso", +"9=lowest" => "9=matalin", +"Empty Summary" => "Tyhjä yhteenveto", +"Invalid priority" => "Virheellinen prioriteetti", +"Add Task" => "Lisää tehtävä", +"Loading tasks..." => "Ladataan tehtäviä...", +"Important" => "Tärkeä", +"More" => "Enemmän", +"Less" => "Vähemmän", +"Delete" => "Poista" +); diff --git a/apps/tasks/l10n/fr.php b/apps/tasks/l10n/fr.php new file mode 100644 index 0000000000..5b0a0321a8 --- /dev/null +++ b/apps/tasks/l10n/fr.php @@ -0,0 +1,24 @@ + "date/heure invalide", +"Tasks" => "Tâches", +"No category" => "Sans catégorie", +"Unspecified" => "Non spécifié", +"1=highest" => "1=le plus important", +"5=medium" => "5=importance moyenne", +"9=lowest" => "9=le moins important", +"Empty Summary" => "Résumé vide", +"Invalid percent complete" => "Pourcentage d'achèvement invalide", +"Invalid priority" => "Priorité invalide", +"Add Task" => "Ajouter une tâche", +"Order Due" => "Echéance tâche", +"Order List" => "Liste tâche", +"Order Complete" => "Tâche réalisée", +"Order Location" => "Lieu", +"Order Priority" => "Priorité", +"Order Label" => "Etiquette tâche", +"Loading tasks..." => "Chargement des tâches…", +"Important" => "Important", +"More" => "Plus", +"Less" => "Moins", +"Delete" => "Supprimer" +); diff --git a/apps/tasks/l10n/it.php b/apps/tasks/l10n/it.php new file mode 100644 index 0000000000..1aac8aac88 --- /dev/null +++ b/apps/tasks/l10n/it.php @@ -0,0 +1,24 @@ + "Ora/Data non valida", +"Tasks" => "Attività", +"No category" => "Nessuna categoria", +"Unspecified" => "Non specificata", +"1=highest" => "1=massima", +"5=medium" => "5=media", +"9=lowest" => "9=minima", +"Empty Summary" => "Riepilogo vuoto", +"Invalid percent complete" => "Percentuale di completamento non valida", +"Invalid priority" => "Priorità non valida", +"Add Task" => "Aggiungi attività", +"Order Due" => "Ordina per scadenza", +"Order List" => "Ordina per elenco", +"Order Complete" => "Ordina per completamento", +"Order Location" => "Ordina per posizione", +"Order Priority" => "Ordina per priorità", +"Order Label" => "Ordina per etichetta", +"Loading tasks..." => "Caricamento attività in corso...", +"Important" => "Importante", +"More" => "Più", +"Less" => "Meno", +"Delete" => "Elimina" +); diff --git a/apps/tasks/l10n/ja_JP.php b/apps/tasks/l10n/ja_JP.php new file mode 100644 index 0000000000..b5b526c595 --- /dev/null +++ b/apps/tasks/l10n/ja_JP.php @@ -0,0 +1,24 @@ + "無効な日付/時刻", +"Tasks" => "タスク", +"No category" => "カテゴリ無し", +"Unspecified" => "未指定", +"1=highest" => "1=高", +"5=medium" => "5=中", +"9=lowest" => "9=低", +"Empty Summary" => "要旨が未記入", +"Invalid percent complete" => "進捗%が不正", +"Invalid priority" => "無効な優先度", +"Add Task" => "タスクを追加", +"Order Due" => "期日で並べ替え", +"Order List" => "リストで並び替え", +"Order Complete" => "完了で並べ替え", +"Order Location" => "場所で並べ替え", +"Order Priority" => "優先度で並べ替え", +"Order Label" => "ラベルで並べ替え", +"Loading tasks..." => "タスクをロード中...", +"Important" => "重要", +"More" => "詳細", +"Less" => "閉じる", +"Delete" => "削除" +); diff --git a/apps/tasks/l10n/lt_LT.php b/apps/tasks/l10n/lt_LT.php new file mode 100644 index 0000000000..7c17fc2224 --- /dev/null +++ b/apps/tasks/l10n/lt_LT.php @@ -0,0 +1,10 @@ + "Netinkama data/laikas", +"No category" => "Be kategorijos", +"Empty Summary" => "Tuščias aprašymas", +"Invalid percent complete" => "Netinkamas baigimo procentas", +"Important" => "Svarbūs", +"More" => "Daugiau", +"Less" => "Mažiau", +"Delete" => "Ištrinti" +); diff --git a/apps/tasks/l10n/nb_NO.php b/apps/tasks/l10n/nb_NO.php new file mode 100644 index 0000000000..a6b398857b --- /dev/null +++ b/apps/tasks/l10n/nb_NO.php @@ -0,0 +1,17 @@ + "feil i dato/klokkeslett", +"Tasks" => "Oppgaver", +"No category" => "Ingen kategori", +"Unspecified" => "Uspesifisert", +"1=highest" => "1=høyest", +"5=medium" => "5=middels", +"9=lowest" => "9=lavest", +"Invalid percent complete" => "Feil i prosent fullført", +"Invalid priority" => "Ulovlig prioritet", +"Add Task" => "Legg til oppgave", +"Loading tasks..." => "Henter oppgaver...", +"Important" => "Viktig", +"More" => "Mer", +"Less" => "Mindre", +"Delete" => "Slett" +); diff --git a/apps/tasks/l10n/pl.php b/apps/tasks/l10n/pl.php new file mode 100644 index 0000000000..4879628486 --- /dev/null +++ b/apps/tasks/l10n/pl.php @@ -0,0 +1,24 @@ + "Zła data/czas", +"Tasks" => "Zadania", +"No category" => "Brak kategorii", +"Unspecified" => "Nieokreślona", +"1=highest" => "1=najwyższy", +"5=medium" => "5=średni", +"9=lowest" => "9=mało ważny ", +"Empty Summary" => "Podsumowanie puste", +"Invalid percent complete" => "Nieprawidłowy procent wykonania", +"Invalid priority" => "Nieprawidłowy priorytet", +"Add Task" => "Dodaj zadanie", +"Order Due" => "Kolejność - domyślna", +"Order List" => "Kolejność - wg lista", +"Order Complete" => "Kolejność - wg kompletności", +"Order Location" => "Kolejność - wg lokalizacja", +"Order Priority" => "Kolejność - wg priorytetu", +"Order Label" => "Kolejność - wg nazywy", +"Loading tasks..." => "Ładuję zadania", +"Important" => "Ważne", +"More" => "Więcej", +"Less" => "Mniej", +"Delete" => "Usuń" +); diff --git a/apps/tasks/l10n/ro.php b/apps/tasks/l10n/ro.php new file mode 100644 index 0000000000..54958582f5 --- /dev/null +++ b/apps/tasks/l10n/ro.php @@ -0,0 +1,24 @@ + "Data/timpul invalid", +"Tasks" => "Sarcini", +"No category" => "Fără categorie", +"Unspecified" => "Nespecificat", +"1=highest" => "1=cel mai înalt", +"5=medium" => "5=mediu", +"9=lowest" => "9=cel mai jos", +"Empty Summary" => "Rezumat gol", +"Invalid percent complete" => "Completare procentuală greșită", +"Invalid priority" => "Prioritare greșită", +"Add Task" => "Adaugă sarcină", +"Order Due" => "Comandă până la", +"Order List" => "Lista de comenzi", +"Order Complete" => "Comandă executată", +"Order Location" => "Locația comenzii", +"Order Priority" => "Prioritarea comenzii", +"Order Label" => "Eticheta comenzii", +"Loading tasks..." => "Încărcare sarcini", +"Important" => "Important", +"More" => "Mai mult", +"Less" => "Mai puțin", +"Delete" => "Șterge" +); diff --git a/apps/tasks/l10n/sl.php b/apps/tasks/l10n/sl.php new file mode 100644 index 0000000000..c235ff0bc4 --- /dev/null +++ b/apps/tasks/l10n/sl.php @@ -0,0 +1,24 @@ + "Neveljaven datum/čas", +"Tasks" => "Opravila", +"No category" => "Ni kategorije", +"Unspecified" => "Nedoločen", +"1=highest" => "1=najvišje", +"5=medium" => "5=srednje", +"9=lowest" => "9=najnižje", +"Empty Summary" => "Prazen povzetek", +"Invalid percent complete" => "Neveljaven odstotek dokončanja", +"Invalid priority" => "Neveljavna prednost", +"Add Task" => "Dodaj opravilo", +"Order Due" => "Razvrsti po roku", +"Order List" => "Razvrsti v seznam", +"Order Complete" => "Razvrsti po zaključenosti", +"Order Location" => "Razvrsti po lokacijah", +"Order Priority" => "Razvrsti po prednosti", +"Order Label" => "Razvrsti po oznakah", +"Loading tasks..." => "Nalagam opravila...", +"Important" => "Pomembno", +"More" => "Več", +"Less" => "Manj", +"Delete" => "Izbriši" +); diff --git a/apps/tasks/l10n/sv.php b/apps/tasks/l10n/sv.php new file mode 100644 index 0000000000..33bab14448 --- /dev/null +++ b/apps/tasks/l10n/sv.php @@ -0,0 +1,24 @@ + "Felaktigt datum/tid", +"Tasks" => "Uppgifter", +"No category" => "Ingen kategori", +"Unspecified" => "Ospecificerad ", +"1=highest" => "1=högsta", +"5=medium" => "5=mellan", +"9=lowest" => "9=lägsta", +"Empty Summary" => "Tom sammanfattning", +"Invalid percent complete" => "Ogiltig andel procent klar", +"Invalid priority" => "Felaktig prioritet", +"Add Task" => "Lägg till uppgift", +"Order Due" => "Förfaller", +"Order List" => "Kategori", +"Order Complete" => "Slutförd", +"Order Location" => "Plats", +"Order Priority" => "Prioritet", +"Order Label" => "Etikett", +"Loading tasks..." => "Laddar uppgifter...", +"Important" => "Viktigt", +"More" => "Mer", +"Less" => "Mindre", +"Delete" => "Radera" +); diff --git a/apps/tasks/l10n/th_TH.php b/apps/tasks/l10n/th_TH.php new file mode 100644 index 0000000000..e4212bf291 --- /dev/null +++ b/apps/tasks/l10n/th_TH.php @@ -0,0 +1,24 @@ + "วันที่ / เวลา ไม่ถูกต้อง", +"Tasks" => "งาน", +"No category" => "ไม่มีหมวดหมู่", +"Unspecified" => "ยังไม่ได้ระบุ", +"1=highest" => "1=สูงสุด", +"5=medium" => "5=ปานกลาง", +"9=lowest" => "9=ต่ำสุด", +"Empty Summary" => "ข้อมูลสรุปยังว่างอยู่", +"Invalid percent complete" => "สัดส่วนเปอร์เซ็นต์ความสมบูรณ์ไม่ถูกต้อง", +"Invalid priority" => "ความสำคัญไม่ถูกต้อง", +"Add Task" => "เพิ่มงานใหม่", +"Order Due" => "จัดเรียงตามกำหนดเวลา", +"Order List" => "จัดเรียงตามรายชื่อ", +"Order Complete" => "จัดเรียงตามความสมบูรณ์", +"Order Location" => "จัดเรียงตามตำแหน่งที่อยู่", +"Order Priority" => "จัดเรียงตามระดับความสำคัญ", +"Order Label" => "จัดเรียงตามป้ายชื่อ", +"Loading tasks..." => "กำลังโหลดข้อมูลงาน...", +"Important" => "สำคัญ", +"More" => "มาก", +"Less" => "น้อย", +"Delete" => "ลบ" +); diff --git a/apps/tasks/lib/app.php b/apps/tasks/lib/app.php index 7b90842033..a97c6b95d1 100644 --- a/apps/tasks/lib/app.php +++ b/apps/tasks/lib/app.php @@ -77,24 +77,24 @@ class OC_Task_App { public static function validateRequest($request) { $errors = array(); - if($request['summary'] == ''){ + if($request['summary'] == '') { $errors['summary'] = self::$l10n->t('Empty Summary'); } try { - $timezone = OCP\Config::getUserValue(OCP\User::getUser(), "calendar", "timezone", "Europe/London"); + $timezone = OC_Calendar_App::getTimezone(); $timezone = new DateTimeZone($timezone); new DateTime($request['due'], $timezone); } catch (Exception $e) { $errors['due'] = self::$l10n->t('Invalid date/time'); } - if ($request['percent_complete'] < 0 || $request['percent_complete'] > 100){ + if ($request['percent_complete'] < 0 || $request['percent_complete'] > 100) { $errors['percent_complete'] = self::$l10n->t('Invalid percent complete'); } - if ($request['percent_complete'] == 100 && !empty($request['completed'])){ + if ($request['percent_complete'] == 100 && !empty($request['completed'])) { try { - $timezone = OCP\Config::getUserValue(OCP\User::getUser(), "calendar", "timezone", "Europe/London"); + $timezone = OC_Calendar_App::getTimezone(); $timezone = new DateTimeZone($timezone); new DateTime($request['completed'], $timezone); } catch (Exception $e) { @@ -147,7 +147,7 @@ class OC_Task_App { $vtodo->setString('PRIORITY', $priority); if ($due) { - $timezone = OCP\Config::getUserValue(OCP\User::getUser(), 'calendar', 'timezone', date_default_timezone_get()); + $timezone = OC_Calendar_App::getTimezone(); $timezone = new DateTimeZone($timezone); $due = new DateTime($due, $timezone); $vtodo->setDateTime('DUE', $due); @@ -168,18 +168,19 @@ class OC_Task_App { $vtodo->__unset('PERCENT-COMPLETE'); } - if ($percent_complete == 100){ - if (!$completed){ + if ($percent_complete == 100) { + if (!$completed) { $completed = 'now'; } } else { $completed = null; } if ($completed) { - $timezone = OCP\Config::getUserValue(OCP\User::getUser(), 'calendar', 'timezone', date_default_timezone_get()); + $timezone = OC_Calendar_App::getTimezone(); $timezone = new DateTimeZone($timezone); $completed = new DateTime($completed, $timezone); $vtodo->setDateTime('COMPLETED', $completed); + OCP\Util::emitHook('OC_Task', 'taskCompleted', $vtodo); } else { unset($vtodo->COMPLETED); } diff --git a/apps/tasks/templates/part.addtaskform.php b/apps/tasks/templates/part.addtaskform.php deleted file mode 100644 index 0fad5592aa..0000000000 --- a/apps/tasks/templates/part.addtaskform.php +++ /dev/null @@ -1,15 +0,0 @@ -
        - - - - - -
        - - inc('part.taskform'); ?> - -
        diff --git a/apps/tasks/templates/part.details.php b/apps/tasks/templates/part.details.php deleted file mode 100644 index 89636b6e76..0000000000 --- a/apps/tasks/templates/part.details.php +++ /dev/null @@ -1,42 +0,0 @@ -SUMMARY)): ?> - -inc('part.property', array('label' => $l->t('Summary'), 'property' => $_['details']->SUMMARY)); -if(isset($_['details']->LOCATION)): - echo $this->inc('part.property', array('label' => $l->t('Location'), 'property' => $_['details']->LOCATION)); -endif; -if(isset($_['details']->CATEGORIES)): - echo $this->inc('part.property', array('label' => $l->t('Categories'), 'property' => $_['details']->CATEGORIES)); -endif; -if(isset($_['details']->DUE)): - echo $this->inc('part.property', array('label' => $l->t('Due'), 'property' => $_['details']->DUE[0])); -endif; -if(isset($_['details']->PRIORITY)): - echo $this->inc('part.property', array('label' => $l->t('Priority'), 'property' => $_['details']->PRIORITY[0], 'options' => $_['priority_options'])); -endif; -if($_['details']->__isset('PERCENT-COMPLETE') || isset($_['details']->COMPLETED)): -?> - - - '; -endif; -if(isset($_['details']->DESCRIPTION)): - echo $this->inc('part.property', array('label' => $l->t('Description'), 'property' => $_['details']->DESCRIPTION)); -endif; ?> -
        - t('Complete') ?> - -__isset('PERCENT-COMPLETE')): - echo $_['details']->__get('PERCENT-COMPLETE')->value.' % '; - endif; - if(isset($_['details']->COMPLETED)): - echo $l->t('on '). $l->l('datetime', $_['details']->COMPLETED[0]->getDateTime()); - endif; - echo '
        -
        - - -
        - - - diff --git a/apps/tasks/templates/part.edittaskform.php b/apps/tasks/templates/part.edittaskform.php deleted file mode 100644 index fe123f07ac..0000000000 --- a/apps/tasks/templates/part.edittaskform.php +++ /dev/null @@ -1,5 +0,0 @@ -
        - - inc('part.taskform'); ?> - -
        diff --git a/apps/tasks/templates/part.property.php b/apps/tasks/templates/part.property.php deleted file mode 100644 index 591fd363e6..0000000000 --- a/apps/tasks/templates/part.property.php +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - l('datetime', $_['property']->getDateTime()); - break; - default: - $value = $_['property']->value; - if (isset($_['options'])) - { - $value = $_['options'][$value]; - } - echo nl2br($value); - } - ?> - - diff --git a/apps/tasks/templates/part.taskform.php b/apps/tasks/templates/part.taskform.php deleted file mode 100644 index c00560903b..0000000000 --- a/apps/tasks/templates/part.taskform.php +++ /dev/null @@ -1,36 +0,0 @@ - - -
        - - -
        - - - <?php echo $l->t('Edit categories'); ?> -
        - - -
        - - - __get('PERCENT-COMPLETE') && $_['details']->__get('PERCENT-COMPLETE')->value == 100) ? '' : ' style="display:none;"' ?>> - -
        - - -
        -
        - -
        diff --git a/apps/tasks/templates/part.tasks.php b/apps/tasks/templates/part.tasks.php deleted file mode 100644 index 50be1cd6be..0000000000 --- a/apps/tasks/templates/part.tasks.php +++ /dev/null @@ -1,3 +0,0 @@ - -
      • - diff --git a/apps/user_external/appinfo/app.php b/apps/user_external/appinfo/app.php new file mode 100644 index 0000000000..c7408ec30d --- /dev/null +++ b/apps/user_external/appinfo/app.php @@ -0,0 +1,4 @@ + + + user_external + External user support + Use external user authentication methods + AGPL + Robin Appelman + 4 + true + + + + diff --git a/apps/user_external/appinfo/version b/apps/user_external/appinfo/version new file mode 100644 index 0000000000..ceab6e11ec --- /dev/null +++ b/apps/user_external/appinfo/version @@ -0,0 +1 @@ +0.1 \ No newline at end of file diff --git a/apps/user_external/lib/ftp.php b/apps/user_external/lib/ftp.php new file mode 100644 index 0000000000..e03e17d2b6 --- /dev/null +++ b/apps/user_external/lib/ftp.php @@ -0,0 +1,45 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +class OC_User_FTP extends OC_User_Backend{ + private $host; + private $secure; + private $protocol; + + public function __construct($host,$secure=false){ + $this->host=$host; + $this->secure=$secure; + $this->protocol='ftp'; + if($this->secure){ + $this->protocol.='s'; + } + $this->protocol.='://'; + } + + /** + * @brief Check if the password is correct + * @param $uid The username + * @param $password The password + * @returns true/false + * + * Check if the password is correct without logging in the user + */ + public function checkPassword($uid, $password){ + $url=$this->protocol.$uid.':'.$password.'@'.$this->host.'/'; + $result=@opendir($url); + if(is_resource($result)){ + return $uid; + }else{ + return false; + } + } + + public function userExists($uid){ + return true; + } +} diff --git a/apps/user_external/lib/imap.php b/apps/user_external/lib/imap.php new file mode 100644 index 0000000000..584e9804b1 --- /dev/null +++ b/apps/user_external/lib/imap.php @@ -0,0 +1,40 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +class OC_User_IMAP extends OC_User_Backend{ + private $mailbox; + + public function __construct($mailbox){ + $this->mailbox=$mailbox; + } + + /** + * @brief Check if the password is correct + * @param $uid The username + * @param $password The password + * @returns true/false + * + * Check if the password is correct without logging in the user + */ + public function checkPassword($uid, $password){ + $mbox = @imap_open($this->mailbox, $uid, $password); + imap_errors(); + imap_alerts(); + if($mbox){ + imap_close($mbox); + return $uid; + }else{ + return false; + } + } + + public function userExists($uid){ + return true; + } +} + diff --git a/apps/user_external/lib/smb.php b/apps/user_external/lib/smb.php new file mode 100644 index 0000000000..44d2b7903d --- /dev/null +++ b/apps/user_external/lib/smb.php @@ -0,0 +1,43 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +class OC_User_SMB extends OC_User_Backend{ + private $host; + + const smbclient='smbclient'; + const loginError='NT_STATUS_LOGON_FAILURE'; + + public function __construct($host){ + $this->host=$host; + } + + /** + * @brief Check if the password is correct + * @param $uid The username + * @param $password The password + * @returns true/false + * + * Check if the password is correct without logging in the user + */ + public function checkPassword($uid, $password){ + $uidEscaped=escapeshellarg($uid); + $password=escapeshellarg($password); + $result=array(); + $command=self::smbclient.' //'.$this->host.'/dummy -U'.$uidEscaped.'%'.$password; + $result=exec($command,$result); + if(substr($result,-strlen(self::loginError))==self::loginError){ + return false; + }else{ + return $uid; + } + } + + public function userExists($uid){ + return true; + } +} \ No newline at end of file diff --git a/apps/user_external/tests/config.php b/apps/user_external/tests/config.php new file mode 100644 index 0000000000..64ee141d32 --- /dev/null +++ b/apps/user_external/tests/config.php @@ -0,0 +1,28 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +return array( + 'imap'=>array( + 'run'=>false, + 'mailbox'=>'{imap.gmail.com:993/imap/ssl}INBOX', //see http://php.net/manual/en/function.imap-open.php + 'user'=>'foo',//valid username/password combination + 'password'=>'bar', + ), + 'smb'=>array( + 'run'=>false, + 'host'=>'localhost', + 'user'=>'test',//valid username/password combination + 'password'=>'test', + ), + 'ftp'=>array( + 'run'=>false, + 'host'=>'localhost', + 'user'=>'test',//valid username/password combination + 'password'=>'test', + ), +); diff --git a/apps/user_external/tests/ftp.php b/apps/user_external/tests/ftp.php new file mode 100644 index 0000000000..0cf7565f9c --- /dev/null +++ b/apps/user_external/tests/ftp.php @@ -0,0 +1,34 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +class Test_User_FTP extends UnitTestCase{ + /** + * @var OC_User_IMAP $instance + */ + private $instance; + + private function getConfig(){ + return include(__DIR__.'/config.php'); + } + + function skip(){ + $config=$this->getConfig(); + $this->skipUnless($config['ftp']['run']); + } + + function setUp(){ + $config=$this->getConfig(); + $this->instance=new OC_User_FTP($config['ftp']['host']); + } + + function testLogin(){ + $config=$this->getConfig(); + $this->assertEqual($config['ftp']['user'],$this->instance->checkPassword($config['ftp']['user'],$config['ftp']['password'])); + $this->assertFalse($this->instance->checkPassword($config['ftp']['user'],$config['ftp']['password'].'foo')); + } +} diff --git a/apps/user_external/tests/imap.php b/apps/user_external/tests/imap.php new file mode 100644 index 0000000000..c703b32107 --- /dev/null +++ b/apps/user_external/tests/imap.php @@ -0,0 +1,34 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +class Test_User_Imap extends UnitTestCase{ + /** + * @var OC_User_IMAP $instance + */ + private $instance; + + private function getConfig(){ + return include(__DIR__.'/config.php'); + } + + function skip(){ + $config=$this->getConfig(); + $this->skipUnless($config['imap']['run']); + } + + function setUp(){ + $config=$this->getConfig(); + $this->instance=new OC_User_IMAP($config['imap']['mailbox']); + } + + function testLogin(){ + $config=$this->getConfig(); + $this->assertEqual($config['imap']['user'],$this->instance->checkPassword($config['imap']['user'],$config['imap']['password'])); + $this->assertFalse($this->instance->checkPassword($config['imap']['user'],$config['imap']['password'].'foo')); + } +} diff --git a/apps/user_external/tests/smb.php b/apps/user_external/tests/smb.php new file mode 100644 index 0000000000..1ed7eb934b --- /dev/null +++ b/apps/user_external/tests/smb.php @@ -0,0 +1,34 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +class Test_User_SMB extends UnitTestCase{ + /** + * @var OC_User_IMAP $instance + */ + private $instance; + + private function getConfig(){ + return include(__DIR__.'/config.php'); + } + + function skip(){ + $config=$this->getConfig(); + $this->skipUnless($config['smb']['run']); + } + + function setUp(){ + $config=$this->getConfig(); + $this->instance=new OC_User_SMB($config['smb']['host']); + } + + function testLogin(){ + $config=$this->getConfig(); + $this->assertEqual($config['smb']['user'],$this->instance->checkPassword($config['smb']['user'],$config['smb']['password'])); + $this->assertFalse($this->instance->checkPassword($config['smb']['user'],$config['smb']['password'].'foo')); + } +} diff --git a/apps/user_ldap/ajax/testConfiguration.php b/apps/user_ldap/ajax/testConfiguration.php new file mode 100644 index 0000000000..a82f7e4c17 --- /dev/null +++ b/apps/user_ldap/ajax/testConfiguration.php @@ -0,0 +1,39 @@ +. + * + */ + +// Check user and app status +OCP\JSON::checkAdminUser(); +OCP\JSON::checkAppEnabled('user_ldap'); +OCP\JSON::callCheck(); + +$connection = new \OCA\user_ldap\lib\Connection(null); +if($connection->setConfiguration($_POST)) { + //Configuration is okay + if($connection->bind()) { + OCP\JSON::success(array('message' => 'The configuration is valid and the connection could be established!')); + } else { + OCP\JSON::error(array('message' => 'The configuration is valid, but the Bind failed. Please check the server settings and credentials.')); + } +} else { + OCP\JSON::error(array('message' => 'The configuration is invalid. Please look in the ownCloud log for further details.')); +} diff --git a/apps/user_ldap/appinfo/app.php b/apps/user_ldap/appinfo/app.php index 330574c1d4..0eec7829a4 100644 --- a/apps/user_ldap/appinfo/app.php +++ b/apps/user_ldap/appinfo/app.php @@ -21,15 +21,17 @@ * */ -require_once('apps/user_ldap/lib_ldap.php'); -require_once('apps/user_ldap/user_ldap.php'); -require_once('apps/user_ldap/group_ldap.php'); +OCP\App::registerAdmin('user_ldap', 'settings'); -OCP\App::registerAdmin('user_ldap','settings'); +$connector = new OCA\user_ldap\lib\Connection('user_ldap'); +$userBackend = new OCA\user_ldap\USER_LDAP(); +$userBackend->setConnector($connector); +$groupBackend = new OCA\user_ldap\GROUP_LDAP(); +$groupBackend->setConnector($connector); // register user backend -OC_User::useBackend( 'LDAP' ); -OC_Group::useBackend( new OC_GROUP_LDAP() ); +OC_User::useBackend($userBackend); +OC_Group::useBackend($groupBackend); // add settings page to navigation $entry = array( @@ -38,3 +40,5 @@ $entry = array( 'href' => OCP\Util::linkTo( 'user_ldap', 'settings.php' ), 'name' => 'LDAP' ); + +OCP\Backgroundjob::addRegularTask('OCA\user_ldap\lib\Jobs', 'updateGroups'); diff --git a/apps/user_ldap/appinfo/database.xml b/apps/user_ldap/appinfo/database.xml index b228fa2796..a785bbf422 100644 --- a/apps/user_ldap/appinfo/database.xml +++ b/apps/user_ldap/appinfo/database.xml @@ -28,6 +28,14 @@ + + directory_uuid + text + true + 255 + + + ldap_dn_users true @@ -71,6 +79,14 @@ + + directory_uuid + text + true + 255 + + + ldap_dn_groups true @@ -92,4 +108,37 @@ + + + + *dbprefix*ldap_group_members + + + + + owncloudname + text + true + 255 + + + + + owncloudusers + clob + true + + + + ldap_group_members + true + + owncloudname + + + + + +
        + \ No newline at end of file diff --git a/apps/user_ldap/appinfo/update.php b/apps/user_ldap/appinfo/update.php index 3ebb19c2fc..9cf1814cf6 100644 --- a/apps/user_ldap/appinfo/update.php +++ b/apps/user_ldap/appinfo/update.php @@ -2,6 +2,11 @@ //from version 0.1 to 0.2 +//ATTENTION +//Upgrade from ownCloud 3 (LDAP backend 0.1) to ownCloud 4.5 (LDAP backend 0.3) is not supported!! +//You must do upgrade to ownCloud 4.0 first! +//The upgrade stuff in the section from 0.1 to 0.2 is just to minimize the bad efffects. + //settings $pw = OCP\Config::getAppValue('user_ldap', 'ldap_password'); if(!is_null($pw)) { @@ -12,25 +17,37 @@ if(!is_null($pw)) { //detect if we can switch on naming guidelines. We won't do it on conflicts. //it's a bit spaghetti, but hey. -$state = OCP\Config::getSystemValue('ldapIgnoreNamingRules', 'doCheck'); -if($state == 'doCheck'){ - $sqlCleanMap = 'DELETE FROM `*PREFIX*ldap_user_mapping`'; - - require_once(OC::$APPSROOT.'/apps/user_ldap/lib_ldap.php'); - require_once(OC::$APPSROOT.'/apps/user_ldap/user_ldap.php'); - - OCP\Config::setSystemValue('ldapIgnoreNamingRules', true); - $LDAP_USER = new OC_USER_LDAP(); - $users_old = $LDAP_USER->getUsers(); - $query = OCP\DB::prepare($sqlCleanMap); - $query->execute(); +$state = OCP\Config::getSystemValue('ldapIgnoreNamingRules', 'unset'); +if($state == 'unset'){ OCP\Config::setSystemValue('ldapIgnoreNamingRules', false); - OC_LDAP::init(true); - $users_new = $LDAP_USER->getUsers(); - $query = OCP\DB::prepare($sqlCleanMap); - $query->execute(); - if($users_old !== $users_new) { - //we don't need to check Groups, because they were not supported in 3' - OCP\Config::setSystemValue('ldapIgnoreNamingRules', true); +} + +// ### SUPPORTED upgrade path starts here ### + +//from version 0.2 to 0.3 (0.2.0.x dev version) +$objects = array('user', 'group'); + +$connector = new \OCA\user_ldap\lib\Connection('user_ldap'); +$userBE = new \OCA\user_ldap\USER_LDAP(); +$userBE->setConnector($connector); +$groupBE = new \OCA\user_ldap\GROUP_LDAP(); +$groupBE->setConnector($connector); + +foreach($objects as $object) { + $fetchDNSql = 'SELECT ldap_dn from *PREFIX*ldap_'.$object.'_mapping'; + $updateSql = 'UPDATE *PREFIX*ldap_'.$object.'_mapping SET ldap_DN = ?, directory_uuid = ? WHERE ldap_dn = ?'; + + $query = OCP\DB::prepare($fetchDNSql); + $res = $query->execute(); + $DNs = $res->fetchAll(); + $updateQuery = OCP\DB::prepare($updateSql); + foreach($DNs as $dn) { + $newDN = mb_strtolower($dn['ldap_dn'], 'UTF-8'); + if($object == 'user') { + $uuid = $userBE->getUUID($newDN); + } else { + $uuid = $groupBE->getUUID($newDN); + } + $updateQuery->execute(array($newDN, $uuid, $dn['ldap_dn'])); } -} \ No newline at end of file +} diff --git a/apps/user_ldap/appinfo/version b/apps/user_ldap/appinfo/version index 2f4536184b..e689e4949e 100644 --- a/apps/user_ldap/appinfo/version +++ b/apps/user_ldap/appinfo/version @@ -1 +1 @@ -0.2 \ No newline at end of file +0.2.0.26 \ No newline at end of file diff --git a/apps/user_ldap/css/settings.css b/apps/user_ldap/css/settings.css new file mode 100644 index 0000000000..30c5c175c9 --- /dev/null +++ b/apps/user_ldap/css/settings.css @@ -0,0 +1,10 @@ +#ldap fieldset p label { + width: 20%; + max-width: 200px; + display: inline-block; +} + +#ldap fieldset input { + width: 70%; + display: inline-block; +} \ No newline at end of file diff --git a/apps/user_ldap/group_ldap.php b/apps/user_ldap/group_ldap.php index a3117b5a41..b29ebe30c5 100644 --- a/apps/user_ldap/group_ldap.php +++ b/apps/user_ldap/group_ldap.php @@ -21,24 +21,17 @@ * */ -class OC_GROUP_LDAP extends OC_Group_Backend { -// //group specific settings - protected $ldapGroupFilter; - protected $ldapGroupMemberAssocAttr; - protected $configured = false; +namespace OCA\user_ldap; - protected $_group_user = array(); - protected $_user_groups = array(); - protected $_group_users = array(); - protected $_groups = array(); +class GROUP_LDAP extends lib\Access implements \OCP\GroupInterface { + protected $enabled = false; - public function __construct() { - $this->ldapGroupFilter = OCP\Config::getAppValue('user_ldap', 'ldap_group_filter', '(objectClass=posixGroup)'); - $this->ldapGroupMemberAssocAttr = OCP\Config::getAppValue('user_ldap', 'ldap_group_member_assoc_attribute', 'uniqueMember'); - - if(!empty($this->ldapGroupFilter) && !empty($this->ldapGroupMemberAssocAttr)) { - $this->configured = true; + public function setConnector(lib\Connection &$connection) { + parent::setConnector($connection); + if(empty($this->connection->ldapGroupFilter) || empty($this->connection->ldapGroupMemberAssocAttr)) { + $this->enabled = false; } + $this->enabled = true; } /** @@ -50,31 +43,33 @@ class OC_GROUP_LDAP extends OC_Group_Backend { * Checks whether the user is member of a group or not. */ public function inGroup($uid, $gid) { - if(!$this->configured) { + if(!$this->enabled) { return false; } - if(isset($this->_group_user[$gid][$uid])) { - return $this->_group_user[$gid][$uid]; + if($this->connection->isCached('inGroup'.$uid.':'.$gid)) { + return $this->connection->getFromCache('inGroup'.$uid.':'.$gid); } - $dn_user = OC_LDAP::username2dn($uid); - $dn_group = OC_LDAP::groupname2dn($gid); + $dn_user = $this->username2dn($uid); + $dn_group = $this->groupname2dn($gid); // just in case if(!$dn_group || !$dn_user) { + $this->connection->writeToCache('inGroup'.$uid.':'.$gid, false); return false; } //usually, LDAP attributes are said to be case insensitive. But there are exceptions of course. - $members = OC_LDAP::readAttribute($dn_group, $this->ldapGroupMemberAssocAttr); + $members = $this->readAttribute($dn_group, $this->connection->ldapGroupMemberAssocAttr); if(!$members) { + $this->connection->writeToCache('inGroup'.$uid.':'.$gid, false); return false; } //extra work if we don't get back user DNs //TODO: this can be done with one LDAP query - if(strtolower($this->ldapGroupMemberAssocAttr) == 'memberuid') { + if(strtolower($this->connection->ldapGroupMemberAssocAttr) == 'memberuid') { $dns = array(); foreach($members as $mid) { - $filter = str_replace('%uid', $mid, OC_LDAP::conf('ldapLoginFilter')); - $ldap_users = OC_LDAP::fetchListOfUsers($filter, 'dn'); + $filter = str_replace('%uid', $mid, $this->connection->ldapLoginFilter); + $ldap_users = $this->fetchListOfUsers($filter, 'dn'); if(count($ldap_users) < 1) { continue; } @@ -83,8 +78,10 @@ class OC_GROUP_LDAP extends OC_Group_Backend { $members = $dns; } - $this->_group_user[$gid][$uid] = in_array($dn_user, $members); - return $this->_group_user[$gid][$uid]; + $isInGroup = in_array($dn_user, $members); + $this->connection->writeToCache('inGroup'.$uid.':'.$gid, $isInGroup); + + return $isInGroup; } /** @@ -96,86 +93,105 @@ class OC_GROUP_LDAP extends OC_Group_Backend { * if the user exists at all. */ public function getUserGroups($uid) { - if(!$this->configured) { + if(!$this->enabled) { return array(); } - if(isset($this->_user_groups[$uid])) { - return $this->_user_groups[$uid]; + if($this->connection->isCached('getUserGroups'.$uid)) { + return $this->connection->getFromCache('getUserGroups'.$uid); } - $userDN = OC_LDAP::username2dn($uid); + $userDN = $this->username2dn($uid); if(!$userDN) { - $this->_user_groups[$uid] = array(); + $this->connection->writeToCache('getUserGroups'.$uid, array()); return array(); } //uniqueMember takes DN, memberuid the uid, so we need to distinguish - if((strtolower($this->ldapGroupMemberAssocAttr) == 'uniquemember') - || (strtolower($this->ldapGroupMemberAssocAttr) == 'member')) { + if((strtolower($this->connection->ldapGroupMemberAssocAttr) == 'uniquemember') + || (strtolower($this->connection->ldapGroupMemberAssocAttr) == 'member') + ) { $uid = $userDN; - } else if(strtolower($this->ldapGroupMemberAssocAttr) == 'memberuid') { - $result = OC_LDAP::readAttribute($userDN, 'uid'); + } else if(strtolower($this->connection->ldapGroupMemberAssocAttr) == 'memberuid') { + $result = $this->readAttribute($userDN, 'uid'); $uid = $result[0]; } else { // just in case $uid = $userDN; } - $filter = OC_LDAP::combineFilterWithAnd(array( - $this->ldapGroupFilter, - $this->ldapGroupMemberAssocAttr.'='.$uid + $filter = $this->combineFilterWithAnd(array( + $this->connection->ldapGroupFilter, + $this->connection->ldapGroupMemberAssocAttr.'='.$uid )); - $groups = OC_LDAP::fetchListOfGroups($filter, array(OC_LDAP::conf('ldapGroupDisplayName'),'dn')); - $this->_user_groups[$uid] = array_unique(OC_LDAP::ownCloudGroupNames($groups), SORT_LOCALE_STRING); + $groups = $this->fetchListOfGroups($filter, array($this->connection->ldapGroupDisplayName,'dn')); + $groups = array_unique($this->ownCloudGroupNames($groups), SORT_LOCALE_STRING); + $this->connection->writeToCache('getUserGroups'.$uid, $groups); - return $this->_user_groups[$uid]; + return $groups; } /** * @brief get a list of all users in a group * @returns array with user ids */ - public function usersInGroup($gid) { - if(!$this->configured) { + public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0) { + if(!$this->enabled) { return array(); } - if(isset($this->_group_users[$gid])) { - return $this->_group_users[$gid]; + $this->groupSearch = $search; + if($this->connection->isCached('usersInGroup'.$gid)) { + $groupUsers = $this->connection->getFromCache('usersInGroup'.$gid); + if(!empty($this->groupSearch)) { + $groupUsers = array_filter($groupUsers, array($this, 'groupMatchesFilter')); + } + if($limit = -1) { + $limit = null; + } + return array_slice($groupUsers, $offset, $limit); } - $groupDN = OC_LDAP::groupname2dn($gid); + $groupDN = $this->groupname2dn($gid); if(!$groupDN) { - $this->_group_users[$gid] = array(); + $this->connection->writeToCache('usersInGroup'.$gid, array()); return array(); } - $members = OC_LDAP::readAttribute($groupDN, $this->ldapGroupMemberAssocAttr); + $members = $this->readAttribute($groupDN, $this->connection->ldapGroupMemberAssocAttr); if(!$members) { - $this->_group_users[$gid] = array(); + $this->connection->writeToCache('usersInGroup'.$gid, array()); return array(); } $result = array(); - $isMemberUid = (strtolower($this->ldapGroupMemberAssocAttr) == 'memberuid'); + $isMemberUid = (strtolower($this->connection->ldapGroupMemberAssocAttr) == 'memberuid'); foreach($members as $member) { if($isMemberUid) { - $filter = str_replace('%uid', $member, OC_LDAP::conf('ldapLoginFilter')); - $ldap_users = OC_LDAP::fetchListOfUsers($filter, 'dn'); + $filter = \OCP\Util::mb_str_replace('%uid', $member, $this->connection->ldapLoginFilter, 'UTF-8'); + $ldap_users = $this->fetchListOfUsers($filter, 'dn'); if(count($ldap_users) < 1) { continue; } - $result[] = OC_LDAP::dn2username($ldap_users[0]); + $result[] = $this->dn2username($ldap_users[0]); continue; } else { - if($ocname = OC_LDAP::dn2username($member)){ + if($ocname = $this->dn2username($member)) { $result[] = $ocname; } } } if(!$isMemberUid) { - $result = array_intersect($result, OCP\User::getUsers()); + $result = array_intersect($result, \OCP\User::getUsers()); } - $this->_group_users[$gid] = array_unique($result, SORT_LOCALE_STRING); - return $this->_group_users[$gid]; + $groupUsers = array_unique($result, SORT_LOCALE_STRING); + $this->connection->writeToCache('usersInGroup'.$gid, $groupUsers); + + if(!empty($this->groupSearch)) { + $groupUsers = array_filter($groupUsers, array($this, 'groupMatchesFilter')); + } + if($limit = -1) { + $limit = null; + } + return array_slice($groupUsers, $offset, $limit); + } /** @@ -184,15 +200,30 @@ class OC_GROUP_LDAP extends OC_Group_Backend { * * Returns a list with all groups */ - public function getGroups() { - if(!$this->configured) { + public function getGroups($search = '', $limit = -1, $offset = 0) { + if(!$this->enabled) { return array(); } - if(empty($this->_groups)) { - $ldap_groups = OC_LDAP::fetchListOfGroups($this->ldapGroupFilter, array(OC_LDAP::conf('ldapGroupDisplayName'), 'dn')); - $this->_groups = OC_LDAP::ownCloudGroupNames($ldap_groups); + + if($this->connection->isCached('getGroups')) { + $ldap_groups = $this->connection->getFromCache('getGroups'); + } else { + $ldap_groups = $this->fetchListOfGroups($this->connection->ldapGroupFilter, array($this->connection->ldapGroupDisplayName, 'dn')); + $ldap_groups = $this->ownCloudGroupNames($ldap_groups); + $this->connection->writeToCache('getGroups', $ldap_groups); } - return $this->_groups; + $this->groupSearch = $search; + if(!empty($this->groupSearch)) { + $ldap_groups = array_filter($ldap_groups, array($this, 'groupMatchesFilter')); + } + if($limit = -1) { + $limit = null; + } + return array_slice($ldap_groups, $offset, $limit); + } + + public function groupMatchesFilter($group) { + return (strripos($group, $this->groupSearch) !== false); } /** @@ -203,4 +234,17 @@ class OC_GROUP_LDAP extends OC_Group_Backend { public function groupExists($gid){ return in_array($gid, $this->getGroups()); } + + /** + * @brief Check if backend implements actions + * @param $actions bitwise-or'ed actions + * @returns boolean + * + * Returns the supported actions as int to be + * compared with OC_USER_BACKEND_CREATE_USER etc. + */ + public function implementsActions($actions) { + //always returns false, because possible actions are modifying actions. We do not write to LDAP, at least for now. + return false; + } } \ No newline at end of file diff --git a/apps/user_ldap/js/settings.js b/apps/user_ldap/js/settings.js index cae9655e3d..7063eead96 100644 --- a/apps/user_ldap/js/settings.js +++ b/apps/user_ldap/js/settings.js @@ -1,3 +1,24 @@ $(document).ready(function() { $('#ldapSettings').tabs(); + $('#ldap_action_test_connection').button(); + $('#ldap_action_test_connection').click(function(event){ + event.preventDefault(); + $.post( + OC.filePath('user_ldap','ajax','testConfiguration.php'), + $('#ldap').serialize(), + function (result) { + if (result.status == 'success') { + OC.dialogs.alert( + result.message, + 'Connection test succeeded' + ); + } else { + OC.dialogs.alert( + result.message, + 'Connection test failed' + ); + } + } + ); + }); }); \ No newline at end of file diff --git a/apps/user_ldap/l10n/.gitkeep b/apps/user_ldap/l10n/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apps/user_ldap/l10n/ca.php b/apps/user_ldap/l10n/ca.php new file mode 100644 index 0000000000..04b0f8997d --- /dev/null +++ b/apps/user_ldap/l10n/ca.php @@ -0,0 +1,36 @@ + "Màquina", +"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Podeu ometre el protocol, excepte si requeriu SSL. Llavors comenceu amb ldaps://", +"Base DN" => "DN Base", +"You can specify Base DN for users and groups in the Advanced tab" => "Podeu especificar DN Base per usuaris i grups a la pestanya Avançat", +"User DN" => "DN Usuari", +"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "La DN de l'usuari client amb la que s'haurà de fer, per exemple uid=agent,dc=exemple,dc=com. Per un accés anònim, deixeu la DN i la contrasenya en blanc.", +"Password" => "Contrasenya", +"For anonymous access, leave DN and Password empty." => "Per un accés anònim, deixeu la DN i la contrasenya en blanc.", +"User Login Filter" => "Filtre d'inici de sessió d'usuari", +"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "Defineix el filtre a aplicar quan s'intenta l'inici de sessió. %%uid reemplaça el nom d'usuari en l'acció d'inici de sessió.", +"use %%uid placeholder, e.g. \"uid=%%uid\"" => "useu el paràmetre de substitució %%uid, per exemple \"uid=%%uid\"", +"User List Filter" => "Llista de filtres d'usuari", +"Defines the filter to apply, when retrieving users." => "Defineix el filtre a aplicar quan es mostren usuaris", +"without any placeholder, e.g. \"objectClass=person\"." => "sense cap paràmetre de substitució, per exemple \"objectClass=persona\"", +"Group Filter" => "Filtre de grup", +"Defines the filter to apply, when retrieving groups." => "Defineix el filtre a aplicar quan es mostren grups.", +"without any placeholder, e.g. \"objectClass=posixGroup\"." => "sense cap paràmetre de substitució, per exemple \"objectClass=grupPosix\".", +"Port" => "Port", +"Base User Tree" => "Arbre base d'usuaris", +"Base Group Tree" => "Arbre base de grups", +"Group-Member association" => "Associació membres-grup", +"Use TLS" => "Usa TLS", +"Do not use it for SSL connections, it will fail." => "No ho useu en connexions SSL, fallarà.", +"Case insensitve LDAP server (Windows)" => "Servidor LDAP sense distinció entre majúscules i minúscules (Windows)", +"Turn off SSL certificate validation." => "Desactiva la validació de certificat SSL.", +"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Si la connexió només funciona amb aquesta opció, importeu el certificat SSL del servidor LDAP en el vostre servidor ownCloud.", +"Not recommended, use for testing only." => "No recomanat, ús només per proves.", +"User Display Name Field" => "Camp per mostrar el nom d'usuari", +"The LDAP attribute to use to generate the user`s ownCloud name." => "Atribut LDAP a usar per generar el nom d'usuari ownCloud.", +"Group Display Name Field" => "Camp per mostrar el nom del grup", +"The LDAP attribute to use to generate the groups`s ownCloud name." => "Atribut LDAP a usar per generar el nom de grup ownCloud.", +"in bytes" => "en bytes", +"in seconds. A change empties the cache." => "en segons. Un canvi buidarà la memòria de cau.", +"Help" => "Ajuda" +); diff --git a/apps/user_ldap/l10n/cs_CZ.php b/apps/user_ldap/l10n/cs_CZ.php new file mode 100644 index 0000000000..6c0f227fa7 --- /dev/null +++ b/apps/user_ldap/l10n/cs_CZ.php @@ -0,0 +1,36 @@ + "Hostitel", +"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Nelze vynechat protokol vyžadující SSL. Začněte s ldaps://", +"Base DN" => "Base DN", +"You can specify Base DN for users and groups in the Advanced tab" => "V Rozšířeném nastavení můžete specifikovat pro své uživatele a skupiny element Base DN", +"User DN" => "DN uživatele", +"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "DN klentského uživatele ke kterému tvoříte vazbu, např. uid=agent,dc=example,dc=com. Pro anonymní přístup ponechte údaje DN and Heslo prázdné.", +"Password" => "Heslo", +"For anonymous access, leave DN and Password empty." => "Pro anonymní přístup ponechte údaje DN and Heslo prázdné.", +"User Login Filter" => "Filtr uživatelských loginů", +"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "Definuje filtr, který je aplikován v průběhu logování. %%uid nahrazuje uživatelské jméno během logování.", +"use %%uid placeholder, e.g. \"uid=%%uid\"" => "použijte %%uid pro rezervované místo, např. \"uid=%%uid\"", +"User List Filter" => "Filtr uživateslkých seznamů", +"Defines the filter to apply, when retrieving users." => "Defunije filtr, který je plaikován při návratu uživatelů.", +"without any placeholder, e.g. \"objectClass=person\"." => "bez rezervace místa, např. \"objectClass=person\".", +"Group Filter" => "Filtr skupiny", +"Defines the filter to apply, when retrieving groups." => "Definuje filtr, který je aplikován při návratu skupin", +"without any placeholder, e.g. \"objectClass=posixGroup\"." => "bez rezervace místa, např. \"objectClass=posixGroup\".", +"Port" => "Port", +"Base User Tree" => "Základní uživatelský strom", +"Base Group Tree" => "Základní skupinový strom", +"Group-Member association" => "Asociace člena skupiny", +"Use TLS" => "Použijte TLS", +"Do not use it for SSL connections, it will fail." => "Nepoužívejte pro SSL připojení, připojení selže.", +"Case insensitve LDAP server (Windows)" => "LDAP server nerozlišující velikost znaků (Windows)", +"Turn off SSL certificate validation." => "Vypněte ověřování SSL certifikátu", +"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "pokud pracuje připojení pouze pokud je teto volba aktivní, importujte SSL certifikát LDAP serveru do Vašeho serveru ownCloud.", +"Not recommended, use for testing only." => "Není doporučeno, pouze pro účely testování.", +"User Display Name Field" => "Pole pro zobrazované jméno uživatele", +"The LDAP attribute to use to generate the user`s ownCloud name." => "Atribut LDAP použitý k vytvoření jména uživatele ownCloud", +"Group Display Name Field" => "Pole pro zobrazení jména skupiny", +"The LDAP attribute to use to generate the groups`s ownCloud name." => "Atribut LDAP použitý k vytvoření jména skupiny ownCloud", +"in bytes" => "v bytech", +"in seconds. A change empties the cache." => "ve vteřinách. Změna vyprázdní dočasnou paměť.", +"Help" => "Nápověda" +); diff --git a/apps/user_ldap/l10n/da.php b/apps/user_ldap/l10n/da.php new file mode 100644 index 0000000000..f01c7b7110 --- /dev/null +++ b/apps/user_ldap/l10n/da.php @@ -0,0 +1,9 @@ + "Host", +"Base DN" => "Base DN", +"Password" => "Kodeord", +"Port" => "Port", +"Use TLS" => "Brug TLS", +"Not recommended, use for testing only." => "Anbefales ikke, brug kun for at teste.", +"Help" => "Hjælp" +); diff --git a/apps/user_ldap/l10n/de.php b/apps/user_ldap/l10n/de.php new file mode 100644 index 0000000000..9f917f277c --- /dev/null +++ b/apps/user_ldap/l10n/de.php @@ -0,0 +1,36 @@ + "Host", +"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Sie können das Protokoll auslassen, außer wenn Sie SSL benötigen. Beginnen Sie dann mit ldaps://", +"Base DN" => "Basis-DN", +"You can specify Base DN for users and groups in the Advanced tab" => "Sie können Basis-DN für Benutzer und Gruppen in dem \"Erweitert\"-Reiter konfigurieren", +"User DN" => "Benutzer-DN", +"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "Der DN des Benutzers für LDAP-Bind, z.B.: uid=agent,dc=example,dc=com. Für anonymen Zugriff lassen Sie DN und Passwort leer.", +"Password" => "Passwort", +"For anonymous access, leave DN and Password empty." => "Lassen Sie die Felder von DN und Passwort für anonymen Zugang leer.", +"User Login Filter" => "Benutzer-Login-Filter", +"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "Bestimmt den angewendeten Filter, wenn eine Anmeldung versucht wird. %%uid ersetzt den Benutzernamen bei dem Anmeldeversuch.", +"use %%uid placeholder, e.g. \"uid=%%uid\"" => "verwende %%uid Platzhalter, z. B. \"uid=%%uid\"", +"User List Filter" => "Benutzer-Filter-Liste", +"Defines the filter to apply, when retrieving users." => "Definiert den Filter für die Anfrage der Benutzer.", +"without any placeholder, e.g. \"objectClass=person\"." => "ohne Platzhalter, z.B.: \"objectClass=person\"", +"Group Filter" => "Gruppen-Filter", +"Defines the filter to apply, when retrieving groups." => "Definiert den Filter für die Anfrage der Gruppen.", +"without any placeholder, e.g. \"objectClass=posixGroup\"." => "ohne Platzhalter, z.B.: \"objectClass=posixGroup\"", +"Port" => "Port", +"Base User Tree" => "Basis-Benutzerbaum", +"Base Group Tree" => "Basis-Gruppenbaum", +"Group-Member association" => "Assoziation zwischen Gruppe und Benutzer", +"Use TLS" => "Nutze TLS", +"Do not use it for SSL connections, it will fail." => "Verwenden Sie es nicht für SSL-Verbindungen, es wird fehlschlagen.", +"Case insensitve LDAP server (Windows)" => "LDAP-Server (Windows: Groß- und Kleinschreibung bleibt unbeachtet)", +"Turn off SSL certificate validation." => "Schalte die SSL Zertifikatsprüfung aus.", +"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Falls die Verbindung es erfordert, wird das SSL-Zertifikat des LDAP-Server importiert werden.", +"Not recommended, use for testing only." => "Nicht empfohlen, nur zu Testzwecken.", +"User Display Name Field" => "Feld für den Anzeigenamen des Benutzers", +"The LDAP attribute to use to generate the user`s ownCloud name." => "Das LDAP-Attribut für die Generierung des Benutzernamens in ownCloud. ", +"Group Display Name Field" => "Feld für den Anzeigenamen der Gruppe", +"The LDAP attribute to use to generate the groups`s ownCloud name." => "Das LDAP-Attribut für die Generierung des Gruppennamens in ownCloud. ", +"in bytes" => "in Bytes", +"in seconds. A change empties the cache." => "in Sekunden. Eine Änderung leert den Cache.", +"Help" => "Hilfe" +); diff --git a/apps/user_ldap/l10n/el.php b/apps/user_ldap/l10n/el.php new file mode 100644 index 0000000000..2f3c747a67 --- /dev/null +++ b/apps/user_ldap/l10n/el.php @@ -0,0 +1,6 @@ + "Συνθηματικό", +"Port" => "Θύρα", +"in bytes" => "σε bytes", +"Help" => "Βοήθεια" +); diff --git a/apps/user_ldap/l10n/eo.php b/apps/user_ldap/l10n/eo.php new file mode 100644 index 0000000000..6f18506b09 --- /dev/null +++ b/apps/user_ldap/l10n/eo.php @@ -0,0 +1,32 @@ + "Gastigo", +"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Vi povas neglekti la protokolon, escepte se vi bezonas SSL-on. Tiuokaze, komencu per ldaps://", +"Base DN" => "Baz-DN", +"User DN" => "Uzanto-DN", +"Password" => "Pasvorto", +"For anonymous access, leave DN and Password empty." => "Por sennoman aliron, lasu DN-on kaj Pasvorton malplenaj.", +"User Login Filter" => "Filtrilo de uzantensaluto", +"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "Ĝi difinas la filtrilon aplikotan, kiam oni provas ensaluti. %%uid anstataŭigas la uzantonomon en la ensaluta ago.", +"use %%uid placeholder, e.g. \"uid=%%uid\"" => "uzu la referencilon %%uid, ekz.: \"uid=%%uid\"", +"User List Filter" => "Filtrilo de uzantolisto", +"Defines the filter to apply, when retrieving users." => "Ĝi difinas la filtrilon aplikotan, kiam veniĝas uzantoj.", +"without any placeholder, e.g. \"objectClass=person\"." => "sen ajna referencilo, ekz.: \"objectClass=person\".", +"Group Filter" => "Filtrilo de grupo", +"Defines the filter to apply, when retrieving groups." => "Ĝi difinas la filtrilon aplikotan, kiam veniĝas grupoj.", +"without any placeholder, e.g. \"objectClass=posixGroup\"." => "sen ajna referencilo, ekz.: \"objectClass=posixGroup\".", +"Port" => "Pordo", +"Base User Tree" => "Baza uzantarbo", +"Base Group Tree" => "Baza gruparbo", +"Use TLS" => "Uzi TLS-on", +"Do not use it for SSL connections, it will fail." => "Ne uzu ĝin por SSL-konektoj, ĝi malsukcesos.", +"Case insensitve LDAP server (Windows)" => "LDAP-servilo blinda je litergrandeco (Vindozo)", +"Turn off SSL certificate validation." => "Malkapabligi validkontrolon de SSL-atestiloj.", +"Not recommended, use for testing only." => "Ne rekomendata, uzu ĝin nur por testoj.", +"User Display Name Field" => "Kampo de vidignomo de uzanto", +"The LDAP attribute to use to generate the user`s ownCloud name." => "La atributo de LDAP uzota por generi la ownCloud-an nomon de la uzanto.", +"Group Display Name Field" => "Kampo de vidignomo de grupo", +"The LDAP attribute to use to generate the groups`s ownCloud name." => "La atributo de LDAP uzota por generi la ownCloud-an nomon de la grupo.", +"in bytes" => "duumoke", +"in seconds. A change empties the cache." => "sekunde. Ajna ŝanĝo malplenigas la kaŝmemoron.", +"Help" => "Helpo" +); diff --git a/apps/user_ldap/l10n/es.php b/apps/user_ldap/l10n/es.php new file mode 100644 index 0000000000..55abf7b88e --- /dev/null +++ b/apps/user_ldap/l10n/es.php @@ -0,0 +1,7 @@ + "Contraseña", +"Port" => "Puerto", +"Use TLS" => "Usar TLS", +"in bytes" => "en bytes", +"Help" => "Ayuda" +); diff --git a/apps/user_ldap/l10n/fi_FI.php b/apps/user_ldap/l10n/fi_FI.php new file mode 100644 index 0000000000..f6d16f3acc --- /dev/null +++ b/apps/user_ldap/l10n/fi_FI.php @@ -0,0 +1,31 @@ + "Isäntä", +"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Voit jättää protokollan määrittämättä, paitsi kun käytät SSL:ää. Aloita silloin ldaps://", +"Base DN" => "Oletus DN", +"You can specify Base DN for users and groups in the Advanced tab" => "Voit määrittää käyttäjien ja ryhmien oletus DN:n (distinguished name) 'tarkemmat asetukset' välilehdeltä ", +"User DN" => "Käyttäjän DN", +"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "Asiakasohjelman DN, jolla yhdistäminen tehdään, ts. uid=agent,dc=example,dc=com. Mahdollistaaksesi anonyymin yhteyden, jätä DN ja salasana tyhjäksi.", +"Password" => "Salasana", +"For anonymous access, leave DN and Password empty." => "Jos haluat mahdollistaa anonyymin pääsyn, jätä DN ja Salasana tyhjäksi ", +"User Login Filter" => "Login suodatus", +"use %%uid placeholder, e.g. \"uid=%%uid\"" => "käytä %%uid paikanvaraajaa, ts. \"uid=%%uid\"", +"User List Filter" => "Käyttäjien suodatus", +"Defines the filter to apply, when retrieving users." => "Määrittelee käytettävän suodattimen, kun käyttäjiä haetaan. ", +"without any placeholder, e.g. \"objectClass=person\"." => "ilman paikanvaraustermiä, ts. \"objectClass=person\".", +"Group Filter" => "Ryhmien suodatus", +"Defines the filter to apply, when retrieving groups." => "Määrittelee käytettävän suodattimen, kun ryhmiä haetaan. ", +"without any placeholder, e.g. \"objectClass=posixGroup\"." => "ilman paikanvaraustermiä, ts. \"objectClass=posixGroup\".", +"Port" => "Portti", +"Base User Tree" => "Oletus käyttäjäpuu", +"Base Group Tree" => "Ryhmien juuri", +"Group-Member association" => "Ryhmä-jäsen assosiaatio (yhteys)", +"Use TLS" => "Käytä TLS:ää", +"Do not use it for SSL connections, it will fail." => "Älä käytä SSL yhteyttä varten, se epäonnistuu. ", +"Case insensitve LDAP server (Windows)" => "Kirjainkoosta piittamaton LDAP-palvelin (Windows)", +"Turn off SSL certificate validation." => "Sulje SSL sertifikaatin käyttö", +"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Jos yhteys toimii vain tällä optiolla, siirrä LDAP palvelimen SSL sertifikaatti onwCloud palvelimellesi. ", +"Not recommended, use for testing only." => "Ei suositella, käytä vain testausta varten.", +"in bytes" => "tavuissa", +"in seconds. A change empties the cache." => "sekunneissa. Muutos tyhjentää välimuistin.", +"Help" => "Ohje" +); diff --git a/apps/user_ldap/l10n/fr.php b/apps/user_ldap/l10n/fr.php new file mode 100644 index 0000000000..64edf3b468 --- /dev/null +++ b/apps/user_ldap/l10n/fr.php @@ -0,0 +1,32 @@ + "Hôte", +"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Vous pouvez omettre le protocole, sauf si vous avez besoin de SSL. Dans ce cas préfixez avec ldaps://", +"Base DN" => "DN de base", +"You can specify Base DN for users and groups in the Advanced tab" => "Vous pouvez spécifier le DN de base pour les utilisateurs et les groupes dans l'onglet Avancé", +"User DN" => "DN Utilisateur", +"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "Le DN de l'utilisateur client avec lequel la liaison doit se faire, par exemple uid=agent,dc=example,dc=com. Pour l'accès anonyme, laisser le DN et le mot de passe vides.", +"Password" => "Mot de passe", +"For anonymous access, leave DN and Password empty." => "Pour l'accès anonyme, laisser le DN et le mot de passe vides.", +"User Login Filter" => "Filtre d'identifiants utilisateur", +"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "Définit le filtre à appliquer lors d'une tentative de connexion. %%uid remplace le nom d'utilisateur lors de la connexion.", +"use %%uid placeholder, e.g. \"uid=%%uid\"" => "veuillez utiliser le champ %%uid , ex.: \"uid=%%uid\"", +"User List Filter" => "Filtre d'utilisateurs", +"Defines the filter to apply, when retrieving users." => "Définit le filtre à appliquer lors de la récupération des utilisateurs.", +"without any placeholder, e.g. \"objectClass=person\"." => "sans élément de substitution, par exemple \"objectClass=person\".", +"Group Filter" => "Filtre de groupes", +"Defines the filter to apply, when retrieving groups." => "Définit le filtre à appliquer lors de la récupération des groupes.", +"without any placeholder, e.g. \"objectClass=posixGroup\"." => "sans élément de substitution, par exemple \"objectClass=posixGroup\".", +"Port" => "Port", +"Group-Member association" => "Association groupe-membre", +"Use TLS" => "Utiliser TLS", +"Do not use it for SSL connections, it will fail." => "Ne pas utiliser pour les connexions SSL, car cela échouera.", +"Case insensitve LDAP server (Windows)" => "Serveur LDAP insensible à la casse (Windows)", +"Turn off SSL certificate validation." => "Désactiver la validation du certificat SSL", +"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Si la connexion ne fonctionne qu'avec cette option, importez le certificat SSL du serveur LDAP dans le serveur ownCloud.", +"Not recommended, use for testing only." => "Non recommendé, utilisation pour tests uniquement.", +"The LDAP attribute to use to generate the user`s ownCloud name." => "L'attribut LDAP utilisé pour générer les noms d'utilisateurs d'ownCloud", +"The LDAP attribute to use to generate the groups`s ownCloud name." => "L'attribut LDAP utilisé pour générer les noms de groupes d'ownCloud", +"in bytes" => "en octets", +"in seconds. A change empties the cache." => "en secondes. Tout changement vide le cache.", +"Help" => "Aide" +); diff --git a/apps/user_ldap/l10n/it.php b/apps/user_ldap/l10n/it.php new file mode 100644 index 0000000000..c86b4ea2a5 --- /dev/null +++ b/apps/user_ldap/l10n/it.php @@ -0,0 +1,36 @@ + "Host", +"You can omit the protocol, except you require SSL. Then start with ldaps://" => "È possibile omettere il protocollo, ad eccezione se è necessario SSL. Quindi inizia con ldaps://", +"Base DN" => "DN base", +"You can specify Base DN for users and groups in the Advanced tab" => "Puoi specificare una DN base per gli utenti ed i gruppi nella scheda Avanzate", +"User DN" => "DN utente", +"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "Il DN per il client dell'utente con cui deve essere associato, ad esempio uid=agent,dc=example,dc=com. Per l'accesso anonimo, lasciare vuoti i campi DN e Password", +"Password" => "Password", +"For anonymous access, leave DN and Password empty." => "Per l'accesso anonimo, lasciare vuoti i campi DN e Password", +"User Login Filter" => "Filtro per l'accesso utente", +"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "Specifica quale filtro utilizzare quando si tenta l'accesso. %%uid sostituisce il nome utente all'atto dell'accesso.", +"use %%uid placeholder, e.g. \"uid=%%uid\"" => "utilizza il segnaposto %%uid, ad esempio \"uid=%%uid\"", +"User List Filter" => "Filtro per l'elenco utenti", +"Defines the filter to apply, when retrieving users." => "Specifica quale filtro utilizzare durante il recupero degli utenti.", +"without any placeholder, e.g. \"objectClass=person\"." => "senza nessun segnaposto, per esempio \"objectClass=person\".", +"Group Filter" => "Filtro per il gruppo", +"Defines the filter to apply, when retrieving groups." => "Specifica quale filtro utilizzare durante il recupero dei gruppi.", +"without any placeholder, e.g. \"objectClass=posixGroup\"." => "senza nessun segnaposto, per esempio \"objectClass=posixGroup\".", +"Port" => "Porta", +"Base User Tree" => "Struttura base dell'utente", +"Base Group Tree" => "Struttura base del gruppo", +"Group-Member association" => "Associazione gruppo-utente ", +"Use TLS" => "Usa TLS", +"Do not use it for SSL connections, it will fail." => "Non utilizzare per le connessioni SSL, fallirà.", +"Case insensitve LDAP server (Windows)" => "Case insensitve LDAP server (Windows)", +"Turn off SSL certificate validation." => "Disattiva il controllo del certificato SSL.", +"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Se la connessione funziona esclusivamente con questa opzione, importa il certificato SSL del server LDAP nel tuo server ownCloud.", +"Not recommended, use for testing only." => "Non consigliato, utilizzare solo per test.", +"User Display Name Field" => "Campo per la visualizzazione del nome utente", +"The LDAP attribute to use to generate the user`s ownCloud name." => "L'attributo LDAP da usare per generare il nome dell'utente ownCloud.", +"Group Display Name Field" => "Campo per la visualizzazione del nome del gruppo", +"The LDAP attribute to use to generate the groups`s ownCloud name." => "L'attributo LDAP da usare per generare il nome del gruppo ownCloud.", +"in bytes" => "in byte", +"in seconds. A change empties the cache." => "in secondi. Il cambio svuota la cache.", +"Help" => "Aiuto" +); diff --git a/apps/user_ldap/l10n/ja_JP.php b/apps/user_ldap/l10n/ja_JP.php new file mode 100644 index 0000000000..8d4473b4e2 --- /dev/null +++ b/apps/user_ldap/l10n/ja_JP.php @@ -0,0 +1,36 @@ + "ホスト", +"You can omit the protocol, except you require SSL. Then start with ldaps://" => "SSL通信しない場合には、プロトコル名を省略することができます。そうでない場合には、ldaps:// から始めてください。", +"Base DN" => "ベースDN", +"You can specify Base DN for users and groups in the Advanced tab" => "拡張タブでユーザとグループのベースDNを指定することができます。", +"User DN" => "ユーザDN", +"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "クライアントユーザーのDNは、特定のものに結びつけることはしません。 例えば uid=agent,dc=example,dc=com. だと匿名アクセスの場合、DNとパスワードは空のままです。", +"Password" => "パスワード", +"For anonymous access, leave DN and Password empty." => "匿名アクセスの場合は、DNとパスワードを空にしてください。", +"User Login Filter" => "ユーザログインフィルタ", +"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "ログインするときに適用するフィルターを定義する。%%uid がログイン時にユーザー名に置き換えられます。", +"use %%uid placeholder, e.g. \"uid=%%uid\"" => "%%uid プレースホルダーを利用してください。例 \"uid=%%uid\"", +"User List Filter" => "ユーザリストフィルタ", +"Defines the filter to apply, when retrieving users." => "ユーザーを取得するときに適用するフィルターを定義する。", +"without any placeholder, e.g. \"objectClass=person\"." => "プレースホルダーを利用しないでください。例 \"objectClass=person\"", +"Group Filter" => "グループフィルタ", +"Defines the filter to apply, when retrieving groups." => "グループを取得するときに適用するフィルターを定義する。", +"without any placeholder, e.g. \"objectClass=posixGroup\"." => "プレースホルダーを利用しないでください。例 \"objectClass=posixGroup\"", +"Port" => "ポート", +"Base User Tree" => "ベースユーザツリー", +"Base Group Tree" => "ベースグループツリー", +"Group-Member association" => "グループとメンバーの関連付け", +"Use TLS" => "TLSを利用", +"Do not use it for SSL connections, it will fail." => "SSL接続に利用しないでください、失敗します。", +"Case insensitve LDAP server (Windows)" => "大文字/小文字を区別しないLDAPサーバ(Windows)", +"Turn off SSL certificate validation." => "SSL証明書の確認を無効にする。", +"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "接続がこのオプションでのみ動作する場合は、LDAPサーバのSSL証明書をownCloudサーバにインポートしてください。", +"Not recommended, use for testing only." => "推奨しません、テスト目的でのみ利用してください。", +"User Display Name Field" => "ユーザ表示名のフィールド", +"The LDAP attribute to use to generate the user`s ownCloud name." => "ユーザのownCloud名の生成に利用するLDAP属性。", +"Group Display Name Field" => "グループ表示名のフィールド", +"The LDAP attribute to use to generate the groups`s ownCloud name." => "グループのownCloud名の生成に利用するLDAP属性。", +"in bytes" => "バイト", +"in seconds. A change empties the cache." => "秒。変更後にキャッシュがクリアされます。", +"Help" => "ヘルプ" +); diff --git a/apps/user_ldap/l10n/lt_LT.php b/apps/user_ldap/l10n/lt_LT.php new file mode 100644 index 0000000000..809ed571bd --- /dev/null +++ b/apps/user_ldap/l10n/lt_LT.php @@ -0,0 +1,9 @@ + "Slaptažodis", +"Group Filter" => "Grupės filtras", +"Port" => "Prievadas", +"Use TLS" => "Naudoti TLS", +"Turn off SSL certificate validation." => "Išjungti SSL sertifikato tikrinimą.", +"Not recommended, use for testing only." => "Nerekomenduojama, naudokite tik testavimui.", +"Help" => "Pagalba" +); diff --git a/apps/user_ldap/l10n/pl.php b/apps/user_ldap/l10n/pl.php new file mode 100644 index 0000000000..fa7618d68d --- /dev/null +++ b/apps/user_ldap/l10n/pl.php @@ -0,0 +1,36 @@ + "Host", +"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Można pominąć protokół, z wyjątkiem wymaganego protokołu SSL. Następnie uruchom z ldaps://", +"Base DN" => "Baza DN", +"You can specify Base DN for users and groups in the Advanced tab" => "Bazę DN można określić dla użytkowników i grup w karcie Zaawansowane", +"User DN" => "Użytkownik DN", +"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "DN użytkownika klienta, z którym powiązanie wykonuje się, np. uid=agent,dc=example,dc=com. Dla dostępu anonimowego pozostawić DN i hasło puste", +"Password" => "Hasło", +"For anonymous access, leave DN and Password empty." => "Dla dostępu anonimowego pozostawić DN i hasło puste.", +"User Login Filter" => "Filtr logowania użytkownika", +"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "Definiuje filtr do zastosowania, gdy podejmowana jest próba logowania. %%uid zastępuje nazwę użytkownika w działaniu logowania.", +"use %%uid placeholder, e.g. \"uid=%%uid\"" => "Użyj %%uid zastępczy, np. \"uid=%%uid\"", +"User List Filter" => "Lista filtrów użytkownika", +"Defines the filter to apply, when retrieving users." => "Definiuje filtry do zastosowania, podczas pobierania użytkowników.", +"without any placeholder, e.g. \"objectClass=person\"." => "bez żadnych symboli zastępczych np. \"objectClass=person\".", +"Group Filter" => "Grupa filtrów", +"Defines the filter to apply, when retrieving groups." => "Definiuje filtry do zastosowania, podczas pobierania grup.", +"without any placeholder, e.g. \"objectClass=posixGroup\"." => "bez żadnych symboli zastępczych np. \"objectClass=posixGroup\".", +"Port" => "Port", +"Base User Tree" => "Drzewo bazy użytkowników", +"Base Group Tree" => "Drzewo bazy grup", +"Group-Member association" => "Członek grupy stowarzyszenia", +"Use TLS" => "Użyj TLS", +"Do not use it for SSL connections, it will fail." => "Nie używaj SSL dla połączeń, jeśli się nie powiedzie.", +"Case insensitve LDAP server (Windows)" => "Wielkość liter serwera LDAP (Windows)", +"Turn off SSL certificate validation." => "Wyłączyć sprawdzanie poprawności certyfikatu SSL.", +"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Jeśli połączenie działa tylko z tą opcją, zaimportuj certyfikat SSL serwera LDAP w serwerze ownCloud.", +"Not recommended, use for testing only." => "Niezalecane, użyj tylko testowo.", +"User Display Name Field" => "Pole wyświetlanej nazwy użytkownika", +"The LDAP attribute to use to generate the user`s ownCloud name." => "Atrybut LDAP służy do generowania nazwy użytkownika ownCloud.", +"Group Display Name Field" => "Pole wyświetlanej nazwy grupy", +"The LDAP attribute to use to generate the groups`s ownCloud name." => "Atrybut LDAP służy do generowania nazwy grup ownCloud.", +"in bytes" => "w bajtach", +"in seconds. A change empties the cache." => "w sekundach. Zmiana opróżnia pamięć podręczną.", +"Help" => "Pomoc" +); diff --git a/apps/user_ldap/l10n/ro.php b/apps/user_ldap/l10n/ro.php new file mode 100644 index 0000000000..326e47f96f --- /dev/null +++ b/apps/user_ldap/l10n/ro.php @@ -0,0 +1,36 @@ + "Gazdă", +"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Puteți omite protocolul, decât dacă folosiți SSL. Atunci se începe cu ldaps://", +"Base DN" => "DN de bază", +"You can specify Base DN for users and groups in the Advanced tab" => "Puteți să specificați DN de bază pentru utilizatori și grupuri în fila Avansat", +"User DN" => "DN al utilizatorului", +"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "DN-ul clientului utilizator cu care se va efectua conectarea, d.e. uid=agent,dc=example,dc=com. Pentru acces anonim, lăsăți DN și Parolă libere.", +"Password" => "Parolă", +"For anonymous access, leave DN and Password empty." => "Pentru acces anonim, lăsați DN și Parolă libere.", +"User Login Filter" => "Filtrare după Nume Utilizator", +"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "Definește fitrele care trebuie aplicate, când se încearcă conectarea. %%uid înlocuiește numele utilizatorului în procesul de conectare.", +"use %%uid placeholder, e.g. \"uid=%%uid\"" => "folosiți substituentul %%uid , d.e. \"uid=%%uid\"", +"User List Filter" => "Filtrarea după lista utilizatorilor", +"Defines the filter to apply, when retrieving users." => "Definește filtrele care trebui aplicate, când se peiau utilzatorii.", +"without any placeholder, e.g. \"objectClass=person\"." => "fără substituenți, d.e. \"objectClass=person\".", +"Group Filter" => "Fitrare Grup", +"Defines the filter to apply, when retrieving groups." => "Definește filtrele care se aplică, când se preiau grupurile.", +"without any placeholder, e.g. \"objectClass=posixGroup\"." => "fără substituenți, d.e. \"objectClass=posixGroup\"", +"Port" => "Portul", +"Base User Tree" => "Arborele de bază al Utilizatorilor", +"Base Group Tree" => "Arborele de bază al Grupurilor", +"Group-Member association" => "Asocierea Grup-Membru", +"Use TLS" => "Utilizează TLS", +"Do not use it for SSL connections, it will fail." => "A nu se utiliza pentru conexiuni SSL, va eșua.", +"Case insensitve LDAP server (Windows)" => "Server LDAP insensibil la majuscule (Windows)", +"Turn off SSL certificate validation." => "Oprește validarea certificatelor SSL ", +"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Dacă conexiunea lucrează doar cu această opțiune, importează certificatul SSL al serverului LDAP în serverul ownCloud.", +"Not recommended, use for testing only." => "Nu este recomandat, a se utiliza doar pentru testare.", +"User Display Name Field" => "Câmpul cu numele vizibil al utilizatorului", +"The LDAP attribute to use to generate the user`s ownCloud name." => "Atributul LDAP folosit pentru a genera numele de utilizator din ownCloud.", +"Group Display Name Field" => "Câmpul cu numele grupului", +"The LDAP attribute to use to generate the groups`s ownCloud name." => "Atributul LDAP folosit pentru a genera numele grupurilor din ownCloud", +"in bytes" => "în octeți", +"in seconds. A change empties the cache." => "în secunde. O schimbare curăță memoria tampon.", +"Help" => "Ajutor" +); diff --git a/apps/user_ldap/l10n/sl.php b/apps/user_ldap/l10n/sl.php new file mode 100644 index 0000000000..312346958b --- /dev/null +++ b/apps/user_ldap/l10n/sl.php @@ -0,0 +1,36 @@ + "Gostitelj", +"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Protokol lahko izpustite, razen če zahtevate SSL. V tem primeru začnite z ldaps://", +"Base DN" => "Osnovni DN", +"You can specify Base DN for users and groups in the Advanced tab" => "Osnovni DN za uporabnike in skupine lahko določite v zavihku Napredno", +"User DN" => "Uporabnik DN", +"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "DN uporabnikovega odjemalca, s katerim naj se opravi vezava, npr. uid=agent,dc=example,dc=com. Za anonimni dostop pustite polji DN in geslo prazni.", +"Password" => "Geslo", +"For anonymous access, leave DN and Password empty." => "Za anonimni dostop pustite polji DN in geslo prazni.", +"User Login Filter" => "Filter prijav uporabnikov", +"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "Določi filter uporabljen pri prijavi. %%uid nadomesti uporaniško ime pri prijavi.", +"use %%uid placeholder, e.g. \"uid=%%uid\"" => "Uporabite ogrado %%uid, npr. \"uid=%%uid\".", +"User List Filter" => "Filter seznama uporabnikov", +"Defines the filter to apply, when retrieving users." => "Določi filter za uporabo med pridobivanjem uporabnikov.", +"without any placeholder, e.g. \"objectClass=person\"." => "Brez katerekoli ograde, npr. \"objectClass=person\".", +"Group Filter" => "Filter skupin", +"Defines the filter to apply, when retrieving groups." => "Določi filter za uporabo med pridobivanjem skupin.", +"without any placeholder, e.g. \"objectClass=posixGroup\"." => "Brez katerekoli ograde, npr. \"objectClass=posixGroup\".", +"Port" => "Vrata", +"Base User Tree" => "Osnovno uporabniško drevo", +"Base Group Tree" => "Osnovno drevo skupine", +"Group-Member association" => "Povezava člana skupine", +"Use TLS" => "Uporabi TLS", +"Do not use it for SSL connections, it will fail." => "Ne uporabljajte ga za SSL povezave, saj ne bo delovalo.", +"Case insensitve LDAP server (Windows)" => "LDAP strežnik je neobčutljiv na velikost črk (Windows)", +"Turn off SSL certificate validation." => "Onemogoči potrditev veljavnosti SSL certifikata.", +"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Če povezava deluje samo s to možnostjo, uvozite SSL potrdilo iz LDAP strežnika v vaš ownCloud strežnik.", +"Not recommended, use for testing only." => "Odsvetovano, uporabite le v namene preizkušanja.", +"User Display Name Field" => "Polje za uporabnikovo prikazano ime", +"The LDAP attribute to use to generate the user`s ownCloud name." => "LDAP atribut uporabljen pri ustvarjanju ownCloud uporabniških imen.", +"Group Display Name Field" => "Polje za prikazano ime skupine", +"The LDAP attribute to use to generate the groups`s ownCloud name." => "LDAP atribut uporabljen pri ustvarjanju ownCloud imen skupin.", +"in bytes" => "v bajtih", +"in seconds. A change empties the cache." => "v sekundah. Sprememba izprazni predpomnilnik.", +"Help" => "Pomoč" +); diff --git a/apps/user_ldap/l10n/sv.php b/apps/user_ldap/l10n/sv.php new file mode 100644 index 0000000000..a23cc094b4 --- /dev/null +++ b/apps/user_ldap/l10n/sv.php @@ -0,0 +1,36 @@ + "Server", +"You can omit the protocol, except you require SSL. Then start with ldaps://" => "Du behöver inte ange protokoll förutom om du använder SSL. Starta då med ldaps://", +"Base DN" => "Start DN", +"You can specify Base DN for users and groups in the Advanced tab" => "Du kan ange start DN för användare och grupper under fliken Avancerat", +"User DN" => "Användare DN", +"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "DN för användaren som skall användas, t.ex. uid=agent, dc=example, dc=com. För anonym åtkomst, lämna DN och lösenord tomt.", +"Password" => "Lösenord", +"For anonymous access, leave DN and Password empty." => "För anonym åtkomst, lämna DN och lösenord tomt.", +"User Login Filter" => "Filter logga in användare", +"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "Definierar filter att tillämpa vid inloggningsförsök. %% uid ersätter användarnamn i loginåtgärden.", +"use %%uid placeholder, e.g. \"uid=%%uid\"" => "använd platshållare %%uid, t ex \"uid=%%uid\"", +"User List Filter" => "Filter lista användare", +"Defines the filter to apply, when retrieving users." => "Definierar filter att tillämpa vid listning av användare.", +"without any placeholder, e.g. \"objectClass=person\"." => "utan platshållare, t.ex. \"objectClass=person\".", +"Group Filter" => "Gruppfilter", +"Defines the filter to apply, when retrieving groups." => "Definierar filter att tillämpa vid listning av grupper.", +"without any placeholder, e.g. \"objectClass=posixGroup\"." => "utan platshållare, t.ex. \"objectClass=posixGroup\".", +"Port" => "Port", +"Base User Tree" => "Bas för användare i katalogtjänst", +"Base Group Tree" => "Bas för grupper i katalogtjänst", +"Group-Member association" => "Attribut för gruppmedlemmar", +"Use TLS" => "Använd TLS", +"Do not use it for SSL connections, it will fail." => "Använd inte för SSL-anslutningar, det kommer inte att fungera.", +"Case insensitve LDAP server (Windows)" => "LDAP-servern är okänslig för gemener och versaler (Windows)", +"Turn off SSL certificate validation." => "Stäng av verifiering av SSL-certifikat.", +"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "Om anslutningen bara fungerar med det här alternativet, importera LDAP-serverns SSL-certifikat i din ownCloud-server.", +"Not recommended, use for testing only." => "Rekommenderas inte, använd bara för test. ", +"User Display Name Field" => "Attribut för användarnamn", +"The LDAP attribute to use to generate the user`s ownCloud name." => "Attribut som används för att generera användarnamn i ownCloud.", +"Group Display Name Field" => "Attribut för gruppnamn", +"The LDAP attribute to use to generate the groups`s ownCloud name." => "Attribut som används för att generera gruppnamn i ownCloud.", +"in bytes" => "i bytes", +"in seconds. A change empties the cache." => "i sekunder. En förändring tömmer cache.", +"Help" => "Hjälp" +); diff --git a/apps/user_ldap/l10n/th_TH.php b/apps/user_ldap/l10n/th_TH.php new file mode 100644 index 0000000000..a1baa64813 --- /dev/null +++ b/apps/user_ldap/l10n/th_TH.php @@ -0,0 +1,36 @@ + "โฮสต์", +"You can omit the protocol, except you require SSL. Then start with ldaps://" => "คุณสามารถปล่อยช่องโปรโตคอลเว้นไว้ได้, ยกเว้นกรณีที่คุณต้องการใช้ SSL จากนั้นเริ่มต้นด้วย ldaps://", +"Base DN" => "DN ฐาน", +"You can specify Base DN for users and groups in the Advanced tab" => "คุณสามารถระบุ DN หลักสำหรับผู้ใช้งานและกลุ่มต่างๆในแท็บขั้นสูงได้", +"User DN" => "DN ของผู้ใช้งาน", +"The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." => "DN ของผู้ใช้งานที่เป็นลูกค้าอะไรก็ตามที่ผูกอยู่ด้วย เช่น uid=agent, dc=example, dc=com, สำหรับการเข้าถึงโดยบุคคลนิรนาม, ให้เว้นว่าง DN และ รหัสผ่านเอาไว้", +"Password" => "รหัสผ่าน", +"For anonymous access, leave DN and Password empty." => "สำหรับการเข้าถึงโดยบุคคลนิรนาม ให้เว้นว่าง DN และรหัสผ่านไว้", +"User Login Filter" => "ตัวกรองข้อมูลการเข้าสู่ระบบของผู้ใช้งาน", +"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action." => "กำหนดตัวกรองข้อมูลที่ต้องการนำไปใช้งาน, เมื่อมีความพยายามในการเข้าสู่ระบบ %%uid จะถูกนำไปแทนที่ชื่อผู้ใช้งานในการกระทำของการเข้าสู่ระบบ", +"use %%uid placeholder, e.g. \"uid=%%uid\"" => "ใช้ตัวยึด %%uid, เช่น \"uid=%%uid\"", +"User List Filter" => "ตัวกรองข้อมูลรายชื่อผู้ใช้งาน", +"Defines the filter to apply, when retrieving users." => "ระบุตัวกรองข้อมูลที่ต้องการนำไปใช้งาน, เมื่อดึงข้อมูลผู้ใช้งาน", +"without any placeholder, e.g. \"objectClass=person\"." => "โดยไม่ต้องมีตัวยึดใดๆ, เช่น \"objectClass=person\",", +"Group Filter" => "ตัวกรองข้อมูลกลุ่ม", +"Defines the filter to apply, when retrieving groups." => "ระบุตัวกรองข้อมูลที่ต้องการนำไปใช้งาน, เมื่อดึงข้อมูลกลุ่ม", +"without any placeholder, e.g. \"objectClass=posixGroup\"." => "โดยไม่ต้องมีตัวยึดใดๆ, เช่น \"objectClass=posixGroup\",", +"Port" => "พอร์ต", +"Base User Tree" => "รายการผู้ใช้งานหลักแบบ Tree", +"Base Group Tree" => "รายการกลุ่มหลักแบบ Tree", +"Group-Member association" => "ความสัมพันธ์ของสมาชิกในกลุ่ม", +"Use TLS" => "ใช้ TLS", +"Do not use it for SSL connections, it will fail." => "กรุณาอย่าใช้การเชื่อมต่อแบบ SSL การเชื่อมต่อจะเกิดการล้มเหลว", +"Case insensitve LDAP server (Windows)" => "เซิร์ฟเวอร์ LDAP ประเภท Case insensitive (วินโดวส์)", +"Turn off SSL certificate validation." => "ปิดใช้งานการตรวจสอบความถูกต้องของใบรับรองความปลอดภัย SSL", +"If connection only works with this option, import the LDAP server's SSL certificate in your ownCloud server." => "หากการเชื่อมต่อสามารถทำงานได้เฉพาะกับตัวเลือกนี้เท่านั้น, ให้นำเข้าข้อมูลใบรับรองความปลอดภัยแบบ SSL ของเซิร์ฟเวอร์ LDAP ดังกล่าวเข้าไปไว้ในเซิร์ฟเวอร์ ownCloud", +"Not recommended, use for testing only." => "ไม่แนะนำให้ใช้งาน, ใช้สำหรับการทดสอบเท่านั้น", +"User Display Name Field" => "ช่องแสดงชื่อผู้ใช้งานที่ต้องการ", +"The LDAP attribute to use to generate the user`s ownCloud name." => "คุณลักษณะ LDAP ที่ต้องการใช้สำหรับสร้างชื่อของผู้ใช้งาน ownCloud", +"Group Display Name Field" => "ช่องแสดงชื่อกลุ่มที่ต้องการ", +"The LDAP attribute to use to generate the groups`s ownCloud name." => "คุณลักษณะ LDAP ที่ต้องการใช้สร้างชื่อกลุ่มของ ownCloud", +"in bytes" => "ในหน่วยไบต์", +"in seconds. A change empties the cache." => "ในอีกไม่กี่วินาที ระบบจะเปลี่ยนแปลงข้อมูลในแคชให้ว่างเปล่า", +"Help" => "ช่วยเหลือ" +); diff --git a/apps/user_ldap/lib/access.php b/apps/user_ldap/lib/access.php new file mode 100644 index 0000000000..be9aa21c3d --- /dev/null +++ b/apps/user_ldap/lib/access.php @@ -0,0 +1,676 @@ +. + * + */ + +namespace OCA\user_ldap\lib; + +abstract class Access { + protected $connection; + + public function setConnector(Connection &$connection) { + $this->connection = $connection; + } + + private function checkConnection() { + return ($this->connection instanceof Connection); + } + + /** + * @brief reads a given attribute for an LDAP record identified by a DN + * @param $dn the record in question + * @param $attr the attribute that shall be retrieved + * @returns the values in an array on success, false otherwise + * + * Reads an attribute from an LDAP entry + */ + public function readAttribute($dn, $attr) { + if(!$this->checkConnection()) { + \OCP\Util::writeLog('user_ldap', 'No LDAP Connector assigned, access impossible for readAttribute.', \OCP\Util::WARN); + return false; + } + $cr = $this->connection->getConnectionResource(); + if(!is_resource($cr)) { + //LDAP not available + \OCP\Util::writeLog('user_ldap', 'LDAP resource not available.', \OCP\Util::DEBUG); + return false; + } + $rr = @ldap_read($cr, $dn, 'objectClass=*', array($attr)); + if(!is_resource($rr)) { + \OCP\Util::writeLog('user_ldap', 'readAttribute '.$attr.' failed for DN '.$dn, \OCP\Util::DEBUG); + //in case an error occurs , e.g. object does not exist + return false; + } + $er = ldap_first_entry($cr, $rr); + //LDAP attributes are not case sensitive + $result = \OCP\Util::mb_array_change_key_case(ldap_get_attributes($cr, $er), MB_CASE_LOWER, 'UTF-8'); + $attr = mb_strtolower($attr, 'UTF-8'); + + if(isset($result[$attr]) && $result[$attr]['count'] > 0) { + $values = array(); + for($i=0;$i<$result[$attr]['count'];$i++) { + $values[] = $this->resemblesDN($attr) ? $this->sanitizeDN($result[$attr][$i]) : $result[$attr][$i]; + } + return $values; + } + \OCP\Util::writeLog('user_ldap', 'Requested attribute '.$attr.' not found for '.$dn, \OCP\Util::DEBUG); + return false; + } + + /** + * @brief checks wether the given attribute`s valua is probably a DN + * @param $attr the attribute in question + * @return if so true, otherwise false + */ + private function resemblesDN($attr) { + $resemblingAttributes = array( + 'dn', + 'uniquemember', + 'member' + ); + return in_array($attr, $resemblingAttributes); + } + + /** + * @brief sanitizes a DN received from the LDAP server + * @param $dn the DN in question + * @return the sanitized DN + */ + private function sanitizeDN($dn) { + //OID sometimes gives back DNs with whitespace after the comma a la "uid=foo, cn=bar, dn=..." We need to tackle this! + $dn = preg_replace('/([^\\\]),(\s+)/u', '\1,', $dn); + + //make comparisons and everything work + $dn = mb_strtolower($dn, 'UTF-8'); + + return $dn; + } + + /** + * gives back the database table for the query + */ + private function getMapTable($isUser) { + if($isUser) { + return '*PREFIX*ldap_user_mapping'; + } else { + return '*PREFIX*ldap_group_mapping'; + } + } + + /** + * @brief returns the LDAP DN for the given internal ownCloud name of the group + * @param $name the ownCloud name in question + * @returns string with the LDAP DN on success, otherwise false + * + * returns the LDAP DN for the given internal ownCloud name of the group + */ + public function groupname2dn($name) { + return $this->ocname2dn($name, false); + } + + /** + * @brief returns the LDAP DN for the given internal ownCloud name of the user + * @param $name the ownCloud name in question + * @returns string with the LDAP DN on success, otherwise false + * + * returns the LDAP DN for the given internal ownCloud name of the user + */ + public function username2dn($name) { + $dn = $this->ocname2dn($name, true); + if($dn) { + return $dn; + } else { + //fallback: user is not mapped + $filter = $this->combineFilterWithAnd(array( + $this->connection->ldapUserFilter, + $this->connection->ldapUserDisplayName . '=' . $name, + )); + $result = $this->searchUsers($filter, 'dn'); + if(isset($result[0]['dn'])) { + $this->mapComponent($result[0], $name, true); + return $result[0]; + } + } + + return false; + } + + /** + * @brief returns the LDAP DN for the given internal ownCloud name + * @param $name the ownCloud name in question + * @param $isUser is it a user? otherwise group + * @returns string with the LDAP DN on success, otherwise false + * + * returns the LDAP DN for the given internal ownCloud name + */ + private function ocname2dn($name, $isUser) { + $table = $this->getMapTable($isUser); + + $query = \OCP\DB::prepare(' + SELECT ldap_dn + FROM '.$table.' + WHERE owncloud_name = ? + '); + + $record = $query->execute(array($name))->fetchOne(); + return $record; + } + + /** + * @brief returns the internal ownCloud name for the given LDAP DN of the group + * @param $dn the dn of the group object + * @param $ldapname optional, the display name of the object + * @returns string with with the name to use in ownCloud, false on DN outside of search DN + * + * returns the internal ownCloud name for the given LDAP DN of the group, false on DN outside of search DN or failure + */ + public function dn2groupname($dn, $ldapname = null) { + if(mb_strripos($dn, $this->sanitizeDN($this->connection->ldapBaseGroups), 0, 'UTF-8') !== (mb_strlen($dn, 'UTF-8')-mb_strlen($this->sanitizeDN($this->connection->ldapBaseGroups), 'UTF-8'))) { + return false; + } + return $this->dn2ocname($dn, $ldapname, false); + } + + /** + * @brief returns the internal ownCloud name for the given LDAP DN of the user + * @param $dn the dn of the user object + * @param $ldapname optional, the display name of the object + * @returns string with with the name to use in ownCloud + * + * returns the internal ownCloud name for the given LDAP DN of the user, false on DN outside of search DN or failure + */ + public function dn2username($dn, $ldapname = null) { + if(mb_strripos($dn, $this->sanitizeDN($this->connection->ldapBaseUsers), 0, 'UTF-8') !== (mb_strlen($dn, 'UTF-8')-mb_strlen($this->sanitizeDN($this->connection->ldapBaseUsers), 'UTF-8'))) { + return false; + } + return $this->dn2ocname($dn, $ldapname, true); + } + + /** + * @brief returns an internal ownCloud name for the given LDAP DN + * @param $dn the dn of the user object + * @param $ldapname optional, the display name of the object + * @param $isUser optional, wether it is a user object (otherwise group assumed) + * @returns string with with the name to use in ownCloud + * + * returns the internal ownCloud name for the given LDAP DN of the user, false on DN outside of search DN + */ + public function dn2ocname($dn, $ldapname = null, $isUser = true) { + $dn = $this->sanitizeDN($dn); + $table = $this->getMapTable($isUser); + if($isUser) { + $nameAttribute = $this->connection->ldapUserDisplayName; + } else { + $nameAttribute = $this->connection->ldapGroupDisplayName; + } + + $query = \OCP\DB::prepare(' + SELECT owncloud_name + FROM '.$table.' + WHERE ldap_dn = ? + '); + + //let's try to retrieve the ownCloud name from the mappings table + $component = $query->execute(array($dn))->fetchOne(); + if($component) { + return $component; + } + + //second try: get the UUID and check if it is known. Then, update the DN and return the name. + $uuid = $this->getUUID($dn); + if($uuid) { + $query = \OCP\DB::prepare(' + SELECT owncloud_name + FROM '.$table.' + WHERE directory_uuid = ? + '); + $component = $query->execute(array($uuid))->fetchOne(); + if($component) { + $query = \OCP\DB::prepare(' + UPDATE '.$table.' + SET ldap_dn = ? + WHERE directory_uuid = ? + '); + $query->execute(array($dn, $uuid)); + return $component; + } + } + + if(is_null($ldapname)) { + $ldapname = $this->readAttribute($dn, $nameAttribute); + if(!isset($ldapname[0]) && empty($ldapname[0])) { + \OCP\Util::writeLog('user_ldap', 'No or empty name for '.$dn.'.', \OCP\Util::INFO); + return false; + } + $ldapname = $ldapname[0]; + } + $ldapname = $this->sanitizeUsername($ldapname); + + //a new user/group! Then let's try to add it. We're shooting into the blue with the user/group name, assuming that in most cases there will not be a conflict. Otherwise an error will occur and we will continue with our second shot. + if($this->mapComponent($dn, $ldapname, $isUser)) { + return $ldapname; + } + + //doh! There is a conflict. We need to distinguish between users/groups. Adding indexes is an idea, but not much of a help for the user. The DN is ugly, but for now the only reasonable way. But we transform it to a readable format and remove the first part to only give the path where this object is located. + $oc_name = $this->alternateOwnCloudName($ldapname, $dn); + if($this->mapComponent($dn, $oc_name, $isUser)) { + return $oc_name; + } + + //if everything else did not help.. + \OCP\Util::writeLog('user_ldap', 'Could not create unique ownCloud name for '.$dn.'.', \OCP\Util::INFO); + } + + /** + * @brief gives back the user names as they are used ownClod internally + * @param $ldapGroups an array with the ldap Users result in style of array ( array ('dn' => foo, 'uid' => bar), ... ) + * @returns an array with the user names to use in ownCloud + * + * gives back the user names as they are used ownClod internally + */ + public function ownCloudUserNames($ldapUsers) { + return $this->ldap2ownCloudNames($ldapUsers, true); + } + + /** + * @brief gives back the group names as they are used ownClod internally + * @param $ldapGroups an array with the ldap Groups result in style of array ( array ('dn' => foo, 'cn' => bar), ... ) + * @returns an array with the group names to use in ownCloud + * + * gives back the group names as they are used ownClod internally + */ + public function ownCloudGroupNames($ldapGroups) { + return $this->ldap2ownCloudNames($ldapGroups, false); + } + + private function ldap2ownCloudNames($ldapObjects, $isUsers) { + if($isUsers) { + $knownObjects = $this->mappedUsers(); + $nameAttribute = $this->connection->ldapUserDisplayName; + } else { + $knownObjects = $this->mappedGroups(); + $nameAttribute = $this->connection->ldapGroupDisplayName; + } + $ownCloudNames = array(); + + foreach($ldapObjects as $ldapObject) { + $key = \OCP\Util::recursiveArraySearch($knownObjects, $ldapObject['dn']); + + //everything is fine when we know the group + if($key !== false) { + $ownCloudNames[] = $knownObjects[$key]['owncloud_name']; + continue; + } + + //we do not take empty usernames + if(!isset($ldapObject[$nameAttribute]) || empty($ldapObject[$nameAttribute])) { + \OCP\Util::writeLog('user_ldap', 'No or empty name for '.$ldapObject['dn'].', skipping.', \OCP\Util::INFO); + continue; + } + + //a new group! Then let's try to add it. We're shooting into the blue with the group name, assuming that in most cases there will not be a conflict. But first make sure, that the display name contains only allowed characters. + $ocname = $this->sanitizeUsername($ldapObject[$nameAttribute]); + if($this->mapComponent($ldapObject['dn'], $ocname, $isUsers)) { + $ownCloudNames[] = $ocname; + continue; + } + + //doh! There is a conflict. We need to distinguish between groups. Adding indexes is an idea, but not much of a help for the user. The DN is ugly, but for now the only reasonable way. But we transform it to a readable format and remove the first part to only give the path where this entry is located. + $ocname = $this->alternateOwnCloudName($ocname, $ldapObject['dn']); + if($this->mapComponent($ldapObject['dn'], $ocname, $isUsers)) { + $ownCloudNames[] = $ocname; + continue; + } + + //if everything else did not help.. + \OCP\Util::writeLog('user_ldap', 'Could not create unique ownCloud name for '.$ldapObject['dn'].', skipping.', \OCP\Util::INFO); + } + return $ownCloudNames; + } + + /** + * @brief creates a hopefully unique name for owncloud based on the display name and the dn of the LDAP object + * @param $name the display name of the object + * @param $dn the dn of the object + * @returns string with with the name to use in ownCloud + * + * creates a hopefully unique name for owncloud based on the display name and the dn of the LDAP object + */ + private function alternateOwnCloudName($name, $dn) { + $ufn = ldap_dn2ufn($dn); + $name = $name . '@' . trim(\OCP\Util::mb_substr_replace($ufn, '', 0, mb_strpos($ufn, ',', 0, 'UTF-8'), 'UTF-8')); + $name = $this->sanitizeUsername($name); + return $name; + } + + /** + * @brief retrieves all known groups from the mappings table + * @returns array with the results + * + * retrieves all known groups from the mappings table + */ + private function mappedGroups() { + return $this->mappedComponents(false); + } + + /** + * @brief retrieves all known users from the mappings table + * @returns array with the results + * + * retrieves all known users from the mappings table + */ + private function mappedUsers() { + return $this->mappedComponents(true); + } + + private function mappedComponents($isUsers) { + $table = $this->getMapTable($isUsers); + + $query = \OCP\DB::prepare(' + SELECT ldap_dn, owncloud_name + FROM '. $table + ); + + return $query->execute()->fetchAll(); + } + + /** + * @brief inserts a new user or group into the mappings table + * @param $dn the record in question + * @param $ocname the name to use in ownCloud + * @param $isUser is it a user or a group? + * @returns true on success, false otherwise + * + * inserts a new user or group into the mappings table + */ + private function mapComponent($dn, $ocname, $isUser = true) { + $table = $this->getMapTable($isUser); + $dn = $this->sanitizeDN($dn); + + $sqlAdjustment = ''; + $dbtype = \OCP\Config::getSystemValue('dbtype'); + if($dbtype == 'mysql') { + $sqlAdjustment = 'FROM dual'; + } + + $insert = \OCP\DB::prepare(' + INSERT INTO '.$table.' (ldap_dn, owncloud_name, directory_uuid) + SELECT ?,?,? + '.$sqlAdjustment.' + WHERE NOT EXISTS ( + SELECT 1 + FROM '.$table.' + WHERE ldap_dn = ? + OR owncloud_name = ?) + '); + + //feed the DB + $res = $insert->execute(array($dn, $ocname, $this->getUUID($dn), $dn, $ocname)); + + if(\OCP\DB::isError($res)) { + return false; + } + + $insRows = $res->numRows(); + + if($insRows == 0) { + return false; + } + + return true; + } + + public function fetchListOfUsers($filter, $attr) { + return $this->fetchList($this->searchUsers($filter, $attr), (count($attr) > 1)); + } + + public function fetchListOfGroups($filter, $attr) { + return $this->fetchList($this->searchGroups($filter, $attr), (count($attr) > 1)); + } + + private function fetchList($list, $manyAttributes) { + if(is_array($list)) { + if($manyAttributes) { + return $list; + } else { + return array_unique($list, SORT_LOCALE_STRING); + } + } + + //error cause actually, maybe throw an exception in future. + return array(); + } + + /** + * @brief executes an LDAP search, optimized for Users + * @param $filter the LDAP filter for the search + * @param $attr optional, when a certain attribute shall be filtered out + * @returns array with the search result + * + * Executes an LDAP search + */ + public function searchUsers($filter, $attr = null) { + return $this->search($filter, $this->connection->ldapBaseUsers, $attr); + } + + /** + * @brief executes an LDAP search, optimized for Groups + * @param $filter the LDAP filter for the search + * @param $attr optional, when a certain attribute shall be filtered out + * @returns array with the search result + * + * Executes an LDAP search + */ + public function searchGroups($filter, $attr = null) { + return $this->search($filter, $this->connection->ldapBaseGroups, $attr); + } + + /** + * @brief executes an LDAP search + * @param $filter the LDAP filter for the search + * @param $base the LDAP subtree that shall be searched + * @param $attr optional, when a certain attribute shall be filtered out + * @returns array with the search result + * + * Executes an LDAP search + */ + private function search($filter, $base, $attr = null) { + if(!is_null($attr) && !is_array($attr)) { + $attr = array(mb_strtolower($attr, 'UTF-8')); + } + + // See if we have a resource + $link_resource = $this->connection->getConnectionResource(); + if(is_resource($link_resource)) { + $sr = ldap_search($link_resource, $base, $filter, $attr); + $findings = ldap_get_entries($link_resource, $sr ); + + // if we're here, probably no connection resource is returned. + // to make ownCloud behave nicely, we simply give back an empty array. + if(is_null($findings)) { + return array(); + } + } else { + // Seems like we didn't find any resource. + // Return an empty array just like before. + \OCP\Util::writeLog('user_ldap', 'Could not search, because resource is missing.', \OCP\Util::DEBUG); + return array(); + } + + if(!is_null($attr)) { + $selection = array(); + $multiarray = false; + if(count($attr) > 1) { + $multiarray = true; + $i = 0; + } + foreach($findings as $item) { + if(!is_array($item)) { + continue; + } + $item = \OCP\Util::mb_array_change_key_case($item, MB_CASE_LOWER, 'UTF-8'); + + if($multiarray) { + foreach($attr as $key) { + $key = mb_strtolower($key, 'UTF-8'); + if(isset($item[$key])) { + if($key != 'dn') { + $selection[$i][$key] = $this->resemblesDN($key) ? $this->sanitizeDN($item[$key][0]) : $item[$key][0]; + } else { + $selection[$i][$key] = $this->sanitizeDN($item[$key]); + } + } + + } + $i++; + } else { + //tribute to case insensitivity + $key = mb_strtolower($attr[0], 'UTF-8'); + + if(isset($item[$key])) { + if($this->resemblesDN($key)) { + $selection[] = $this->sanitizeDN($item[$key]); + } else { + $selection[] = $item[$key]; + } + } + } + } + return $selection; + } + return $findings; + } + + public function sanitizeUsername($name) { + if($this->connection->ldapIgnoreNamingRules) { + return $name; + } + + //REPLACEMENTS + $name = \OCP\Util::mb_str_replace(' ', '_', $name, 'UTF-8'); + + //every remaining unallowed characters will be removed + $name = preg_replace('/[^a-zA-Z0-9_.@-]/u', '', $name); + + return $name; + } + + /** + * @brief combines the input filters with AND + * @param $filters array, the filters to connect + * @returns the combined filter + * + * Combines Filter arguments with AND + */ + public function combineFilterWithAnd($filters) { + return $this->combineFilter($filters, '&'); + } + + /** + * @brief combines the input filters with AND + * @param $filters array, the filters to connect + * @returns the combined filter + * + * Combines Filter arguments with AND + */ + public function combineFilterWithOr($filters) { + return $this->combineFilter($filters, '|'); + } + + /** + * @brief combines the input filters with given operator + * @param $filters array, the filters to connect + * @param $operator either & or | + * @returns the combined filter + * + * Combines Filter arguments with AND + */ + private function combineFilter($filters, $operator) { + $combinedFilter = '('.$operator; + foreach($filters as $filter) { + if($filter[0] != '(') { + $filter = '('.$filter.')'; + } + $combinedFilter.=$filter; + } + $combinedFilter.=')'; + return $combinedFilter; + } + + public function areCredentialsValid($name, $password) { + $testConnection = clone $this->connection; + $credentials = array( + 'ldapAgentName' => $name, + 'ldapAgentPassword' => $password + ); + if(!$testConnection->setConfiguration($credentials)) { + return false; + } + return $testConnection->bind(); + } + + /** + * @brief auto-detects the directory's UUID attribute + * @param $dn a known DN used to check against + * @param $force the detection should be run, even if it is not set to auto + * @returns true on success, false otherwise + */ + private function detectUuidAttribute($dn, $force = false) { + if(($this->connection->ldapUuidAttribute != 'auto') && !$force) { + return true; + } + + //for now, supported (known) attributes are entryUUID, nsuniqueid, objectGUID + $testAttributes = array('entryuuid', 'nsuniqueid', 'objectguid'); + + foreach($testAttributes as $attribute) { + \OCP\Util::writeLog('user_ldap', 'Testing '.$attribute.' as UUID attr', \OCP\Util::DEBUG); + + $value = $this->readAttribute($dn, $attribute); + if(is_array($value) && isset($value[0]) && !empty($value[0])) { + \OCP\Util::writeLog('user_ldap', 'Setting '.$attribute.' as UUID attr', \OCP\Util::DEBUG); + $this->connection->ldapUuidAttribute = $attribute; + return true; + } + \OCP\Util::writeLog('user_ldap', 'The looked for uuid attr is not '.$attribute.', result was '.print_r($value,true), \OCP\Util::DEBUG); + } + + return false; + } + + public function getUUID($dn) { + if($this->detectUuidAttribute($dn)) { + $uuid = $this->readAttribute($dn, $this->connection->ldapUuidAttribute); + if(!is_array($uuid) && $this->connection->ldapOverrideUuidAttribute) { + $this->detectUuidAttribute($dn, true); + $uuid = $this->readAttribute($dn, $this->connection->ldapUuidAttribute); + } + if(is_array($uuid) && isset($uuid[0]) && !empty($uuid[0])) { + $uuid = $uuid[0]; + } else { + $uuid = false; + } + } else { + $uuid = false; + } + return $uuid; + } +} \ No newline at end of file diff --git a/apps/user_ldap/lib/connection.php b/apps/user_ldap/lib/connection.php new file mode 100644 index 0000000000..dc160a1642 --- /dev/null +++ b/apps/user_ldap/lib/connection.php @@ -0,0 +1,358 @@ +. + * + */ + +namespace OCA\user_ldap\lib; + +class Connection { + private $ldapConnectionRes = null; + private $configID; + private $configured = false; + + //cache handler + protected $cache; + + //settings + protected $config = array( + 'ldapHost' => null, + 'ldapPort' => null, + 'ldapBase' => null, + 'ldapBaseUsers' => null, + 'ldapBaseGroups' => null, + 'ldapAgentName' => null, + 'ldapAgentPassword' => null, + 'ldapTLS' => null, + 'ldapNoCase' => null, + 'turnOffCertCheck' => null, + 'ldapIgnoreNamingRules' => null, + 'ldapUserDisplayName' => null, + 'ldapUserFilter' => null, + 'ldapGroupFilter' => null, + 'ldapGroupDisplayName' => null, + 'ldapLoginFilter' => null, + 'ldapQuotaAttribute' => null, + 'ldapQuotaDefault' => null, + 'ldapEmailAttribute' => null, + 'ldapCacheTTL' => null, + 'ldapUuidAttribute' => null, + 'ldapOverrideUuidAttribute' => null, + ); + + public function __construct($configID = 'user_ldap') { + $this->configID = $configID; + $this->cache = \OC_Cache::getGlobalCache(); + } + + public function __destruct() { + @ldap_unbind($this->ldapConnectionRes); + } + + public function __get($name) { + if(!$this->configured) { + $this->readConfiguration(); + } + + if(isset($this->config[$name])) { + return $this->config[$name]; + } + } + + public function __set($name, $value) { + $changed = false; + //omly few options are writable + if($name == 'ldapUuidAttribute') { + \OCP\Util::writeLog('user_ldap', 'Set config ldapUuidAttribute to '.$value, \OCP\Util::DEBUG); + $this->config[$name] = $value; + if(!empty($this->configID)) { + \OCP\Config::getAppValue($this->configID, 'ldap_uuid_attribute', $value); + } + $changed = true; + } + if($changed) { + $this->validateConfiguration(); + } + } + + /** + * @brief initializes the LDAP backend + * @param $force read the config settings no matter what + * + * initializes the LDAP backend + */ + public function init($force = false) { + $this->readConfiguration($force); + $this->establishConnection(); + } + + /** + * Returns the LDAP handler + */ + public function getConnectionResource() { + if(!$this->ldapConnectionRes) { + $this->init(); + } else if(!is_resource($this->ldapConnectionRes)) { + $this->ldapConnectionRes = null; + $this->establishConnection(); + } + if(is_null($this->ldapConnectionRes)) { + \OCP\Util::writeLog('user_ldap', 'Connection could not be established', \OCP\Util::ERROR); + } + return $this->ldapConnectionRes; + } + + private function getCacheKey($key) { + $prefix = 'LDAP-'.$this->configID.'-'; + if(is_null($key)) { + return $prefix; + } + return $prefix.md5($key); + } + + public function getFromCache($key) { + if(!$this->configured) { + $this->readConfiguration(); + } + if(!$this->config['ldapCacheTTL']) { + return null; + } + if(!$this->isCached($key)) { + return null; + + } + $key = $this->getCacheKey($key); + + return unserialize(base64_decode($this->cache->get($key))); + } + + public function isCached($key) { + if(!$this->configured) { + $this->readConfiguration(); + } + if(!$this->config['ldapCacheTTL']) { + return false; + } + $key = $this->getCacheKey($key); + return $this->cache->hasKey($key); + } + + public function writeToCache($key, $value) { + if(!$this->configured) { + $this->readConfiguration(); + } + if(!$this->config['ldapCacheTTL']) { + return null; + } + $key = $this->getCacheKey($key); + $value = base64_encode(serialize($value)); + $this->cache->set($key, $value, $this->config['ldapCacheTTL']); + } + + public function clearCache() { + $this->cache->clear($this->getCacheKey(null)); + } + + /** + * Caches the general LDAP configuration. + */ + private function readConfiguration($force = false) { + \OCP\Util::writeLog('user_ldap','Checking conf state: isConfigured? '.print_r($this->configured, true).' isForce? '.print_r($force, true).' configID? '.print_r($this->configID, true), \OCP\Util::DEBUG); + if((!$this->configured || $force) && !is_null($this->configID)) { + \OCP\Util::writeLog('user_ldap','Reading the configuration', \OCP\Util::DEBUG); + $this->config['ldapHost'] = \OCP\Config::getAppValue($this->configID, 'ldap_host', ''); + $this->config['ldapPort'] = \OCP\Config::getAppValue($this->configID, 'ldap_port', 389); + $this->config['ldapAgentName'] = \OCP\Config::getAppValue($this->configID, 'ldap_dn',''); + $this->config['ldapAgentPassword'] = base64_decode(\OCP\Config::getAppValue($this->configID, 'ldap_agent_password','')); + $this->config['ldapBase'] = \OCP\Config::getAppValue($this->configID, 'ldap_base', ''); + $this->config['ldapBaseUsers'] = \OCP\Config::getAppValue($this->configID, 'ldap_base_users',$this->config['ldapBase']); + $this->config['ldapBaseGroups'] = \OCP\Config::getAppValue($this->configID, 'ldap_base_groups', $this->config['ldapBase']); + $this->config['ldapTLS'] = \OCP\Config::getAppValue($this->configID, 'ldap_tls',0); + $this->config['ldapNoCase'] = \OCP\Config::getAppValue($this->configID, 'ldap_nocase', 0); + $this->config['turnOffCertCheck'] = \OCP\Config::getAppValue($this->configID, 'ldap_turn_off_cert_check', 0); + $this->config['ldapUserDisplayName'] = mb_strtolower(\OCP\Config::getAppValue($this->configID, 'ldap_display_name', 'uid'), 'UTF-8'); + $this->config['ldapUserFilter'] = \OCP\Config::getAppValue($this->configID, 'ldap_userlist_filter','objectClass=person'); + $this->config['ldapGroupFilter'] = \OCP\Config::getAppValue($this->configID, 'ldap_group_filter','(objectClass=posixGroup)'); + $this->config['ldapLoginFilter'] = \OCP\Config::getAppValue($this->configID, 'ldap_login_filter', '(uid=%uid)'); + $this->config['ldapGroupDisplayName'] = mb_strtolower(\OCP\Config::getAppValue($this->configID, 'ldap_group_display_name', 'uid'), 'UTF-8'); + $this->config['ldapQuotaAttribute'] = \OCP\Config::getAppValue($this->configID, 'ldap_quota_attr', ''); + $this->config['ldapQuotaDefault'] = \OCP\Config::getAppValue($this->configID, 'ldap_quota_def', ''); + $this->config['ldapEmailAttribute'] = \OCP\Config::getAppValue($this->configID, 'ldap_email_attr', ''); + $this->config['ldapGroupMemberAssocAttr'] = \OCP\Config::getAppValue($this->configID, 'ldap_group_member_assoc_attribute', 'uniqueMember'); + $this->config['ldapIgnoreNamingRules'] = \OCP\Config::getSystemValue('ldapIgnoreNamingRules', false); + $this->config['ldapCacheTTL'] = \OCP\Config::getAppValue($this->configID, 'ldap_cache_ttl', 10*60); + $this->config['ldapUuidAttribute'] = \OCP\Config::getAppValue($this->configID, 'ldap_uuid_attribute', 'auto'); + $this->config['ldapOverrideUuidAttribute'] = \OCP\Config::getAppValue($this->configID, 'ldap_override_uuid_attribute', 0); + + $this->configured = $this->validateConfiguration(); + } + } + + /** + * @brief set LDAP configuration with values delivered by an array, not read from configuration + * @param $config array that holds the config parameters in an associated array + * @param &$setParameters optional; array where the set fields will be given to + * @return true if config validates, false otherwise. Check with $setParameters for detailed success on single parameters + */ + public function setConfiguration($config, &$setParameters = null) { + if(!is_array($config)) { + return false; + } + + $params = array('ldap_host'=>'ldapHost', 'ldap_port'=>'ldapPort', 'ldap_dn'=>'ldapAgentName', 'ldap_agent_password'=>'ldapAgentPassword', 'ldap_base'=>'ldapBase', 'ldap_base_users'=>'ldapBaseUsers', 'ldap_base_groups'=>'ldapBaseGroups', 'ldap_userlist_filter'=>'ldapUserFilter', 'ldap_login_filter'=>'ldapLoginFilter', 'ldap_group_filter'=>'ldapGroupFilter', 'ldap_display_name'=>'ldapUserDisplayName', 'ldap_group_display_name'=>'ldapGroupDisplayName', + + 'ldap_tls'=>'ldapTLS', 'ldap_nocase'=>'ldapNoCase', 'ldap_quota_def'=>'ldapQuotaDefault', 'ldap_quota_attr'=>'ldapQuotaAttribute', 'ldap_email_attr'=>'ldapEmailAttribute', 'ldap_group_member_assoc_attribute'=>'ldapGroupMemberAssocAttr', 'ldap_cache_ttl'=>'ldapCacheTTL'); + + foreach($config as $parameter => $value) { + if(isset($this->config[$parameter])) { + $this->config[$parameter] = $value; + if(is_array($setParameters)) { + $setParameters[] = $parameter; + } + } else if(isset($params[$parameter])) { + $this->config[$params[$parameter]] = $value; + if(is_array($setParameters)) { + $setParameters[] = $params[$parameter]; + } + } + } + + $this->configured = $this->validateConfiguration(); + + return $this->configured; + } + + /** + * @brief Validates the user specified configuration + * @returns true if configuration seems OK, false otherwise + */ + private function validateConfiguration() { + //first step: "soft" checks: settings that are not really necessary, but advisable. If left empty, give an info message + if(empty($this->config['ldapBaseUsers'])) { + \OCP\Util::writeLog('user_ldap', 'Base tree for Users is empty, using Base DN', \OCP\Util::INFO); + $this->config['ldapBaseUsers'] = $this->config['ldapBase']; + } + if(empty($this->config['ldapBaseGroups'])) { + \OCP\Util::writeLog('user_ldap', 'Base tree for Groups is empty, using Base DN', \OCP\Util::INFO); + $this->config['ldapBaseGroups'] = $this->config['ldapBase']; + } + if(empty($this->config['ldapGroupFilter']) && empty($this->config['ldapGroupMemberAssocAttr'])) { + \OCP\Util::writeLog('user_ldap', 'No group filter is specified, LDAP group feature will not be used.', \OCP\Util::INFO); + } + if(!in_array($this->config['ldapUuidAttribute'], array('auto','entryuuid', 'nsuniqueid', 'objectguid'))) { + \OCP\Config::setAppValue($this->configID, 'ldap_uuid_attribute', 'auto'); + \OCP\Util::writeLog('user_ldap', 'Illegal value for the UUID Attribute, reset to autodetect.', \OCP\Util::INFO); + } + + + //second step: critical checks. If left empty or filled wrong, set as unconfigured and give a warning. + $configurationOK = true; + if(empty($this->config['ldapHost'])) { + \OCP\Util::writeLog('user_ldap', 'No LDAP host given, won`t connect.', \OCP\Util::WARN); + $configurationOK = false; + } + if(empty($this->config['ldapPort'])) { + \OCP\Util::writeLog('user_ldap', 'No LDAP Port given, won`t connect.', \OCP\Util::WARN); + $configurationOK = false; + } + if((empty($this->config['ldapAgentName']) && !empty($this->config['ldapAgentPassword'])) + || (!empty($this->config['ldapAgentName']) && empty($this->config['ldapAgentPassword']))) { + \OCP\Util::writeLog('user_ldap', 'Either no password given for the user agent or a password is given, but no LDAP agent; won`t connect.', \OCP\Util::WARN); + $configurationOK = false; + } + //TODO: check if ldapAgentName is in DN form + if(empty($this->config['ldapBase']) && (empty($this->config['ldapBaseUsers']) && empty($this->config['ldapBaseGroups']))) { + \OCP\Util::writeLog('user_ldap', 'No Base DN given, won`t connect.', \OCP\Util::WARN); + $configurationOK = false; + } + if(empty($this->config['ldapUserDisplayName'])) { + \OCP\Util::writeLog('user_ldap', 'No user display name attribute specified, won`t connect.', \OCP\Util::WARN); + $configurationOK = false; + } + if(empty($this->config['ldapGroupDisplayName'])) { + \OCP\Util::writeLog('user_ldap', 'No group display name attribute specified, won`t connect.', \OCP\Util::WARN); + $configurationOK = false; + } + if(empty($this->config['ldapLoginFilter'])) { + \OCP\Util::writeLog('user_ldap', 'No login filter specified, won`t connect.', \OCP\Util::WARN); + $configurationOK = false; + } + if(mb_strpos($this->config['ldapLoginFilter'], '%uid', 0, 'UTF-8') === false) { + \OCP\Util::writeLog('user_ldap', 'Login filter does not contain %uid place holder, won`t connect.', \OCP\Util::WARN); + \OCP\Util::writeLog('user_ldap', 'Login filter was ' . $this->config['ldapLoginFilter'], \OCP\Util::DEBUG); + $configurationOK = false; + } + + return $configurationOK; + } + + /** + * Connects and Binds to LDAP + */ + private function establishConnection() { + static $phpLDAPinstalled = true; + if(!$phpLDAPinstalled) { + return false; + } + if(!$this->configured) { + \OCP\Util::writeLog('user_ldap', 'Configuration is invalid, cannot connect', \OCP\Util::WARN); + return false; + } + if(!$this->ldapConnectionRes) { + if(!function_exists('ldap_connect')) { + $phpLDAPinstalled = false; + \OCP\Util::writeLog('user_ldap', 'function ldap_connect is not available. Make sure that the PHP ldap module is installed.', \OCP\Util::ERROR); + + return false; + } + if($this->config['turnOffCertCheck']) { + if(putenv('LDAPTLS_REQCERT=never')) { + \OCP\Util::writeLog('user_ldap', 'Turned off SSL certificate validation successfully.', \OCP\Util::WARN); + } else { + \OCP\Util::writeLog('user_ldap', 'Could not turn off SSL certificate validation.', \OCP\Util::WARN); + } + } + $this->ldapConnectionRes = ldap_connect($this->config['ldapHost'], $this->config['ldapPort']); + if(ldap_set_option($this->ldapConnectionRes, LDAP_OPT_PROTOCOL_VERSION, 3)) { + if(ldap_set_option($this->ldapConnectionRes, LDAP_OPT_REFERRALS, 0)) { + if($this->config['ldapTLS']) { + ldap_start_tls($this->ldapConnectionRes); + } + } + } + + return $this->bind(); + } + } + + /** + * Binds to LDAP + */ + public function bind() { + $ldapLogin = @ldap_bind($this->getConnectionResource(), $this->config['ldapAgentName'], $this->config['ldapAgentPassword']); + if(!$ldapLogin) { + \OCP\Util::writeLog('user_ldap', 'Bind failed: ' . ldap_errno($this->ldapConnectionRes) . ': ' . ldap_error($this->ldapConnectionRes), \OCP\Util::ERROR); + $this->ldapConnectionRes = null; + return false; + } + return true; + } + +} \ No newline at end of file diff --git a/apps/user_ldap/lib/jobs.php b/apps/user_ldap/lib/jobs.php new file mode 100644 index 0000000000..d478731b84 --- /dev/null +++ b/apps/user_ldap/lib/jobs.php @@ -0,0 +1,157 @@ +. + * + */ + +namespace OCA\user_ldap\lib; + +class Jobs { + static private $groupsFromDB; + + static private $groupBE; + static private $connector; + + static public function updateGroups() { + \OCP\Util::writeLog('user_ldap', 'Run background job "updateGroups"', \OCP\Util::DEBUG); + $lastUpdate = \OCP\Config::getAppValue('user_ldap', 'bgjUpdateGroupsLastRun', 0); + if((time() - $lastUpdate) < self::getRefreshInterval()) { + \OCP\Util::writeLog('user_ldap', 'bgJ "updateGroups" – last run too fresh, aborting.', \OCP\Util::DEBUG); + //komm runter Werner die Maurer geben ein aus + return; + } + + $knownGroups = array_keys(self::getKnownGroups()); + $actualGroups = self::getGroupBE()->getGroups(); + + if(empty($actualGroups) && empty($knownGroups)) { + \OCP\Util::writeLog('user_ldap', 'bgJ "updateGroups" – groups do not seem to be configured properly, aborting.', \OCP\Util::INFO); + \OCP\setAppValue('user_ldap', 'bgjUpdateGroupsLastRun', time()); + return; + } + + self::handleKnownGroups(array_intersect($actualGroups, $knownGroups)); + self::handleCreatedGroups(array_diff($actualGroups, $knownGroups)); + self::handleRemovedGroups(array_diff($knownGroups, $actualGroups)); + + \OCP\Config::setAppValue('user_ldap', 'bgjUpdateGroupsLastRun', time()); + + \OCP\Util::writeLog('user_ldap', 'bgJ "updateGroups" – Finished.', \OCP\Util::DEBUG); + } + + static private function getRefreshInterval() { + //defaults to every hour + return \OCP\Config::getAppValue('user_ldap', 'bgjRefreshInterval', 3600); + } + + static private function handleKnownGroups($groups) { + \OCP\Util::writeLog('user_ldap', 'bgJ "updateGroups" – Dealing with known Groups.', \OCP\Util::DEBUG); + $query = \OCP\DB::prepare(' + UPDATE *PREFIX*ldap_group_members + SET owncloudusers = ? + WHERE owncloudname = ? + '); + foreach($groups as $group) { + //we assume, that self::$groupsFromDB has been retrieved already + $knownUsers = unserialize(self::$groupsFromDB[$group]['owncloudusers']); + $actualUsers = self::getGroupBE()->usersInGroup($group); + $hasChanged = false; + foreach(array_diff($knownUsers, $actualUsers) as $removedUser) { + \OCP\Util::emitHook('OC_User', 'post_removeFromGroup', array('uid' => $removedUser, 'gid' => $group)); + \OCP\Util::writeLog('user_ldap', 'bgJ "updateGroups" – "'.$removedUser.'" removed from "'.$group.'".', \OCP\Util::INFO); + $hasChanged = true; + } + foreach(array_diff($actualUsers, $knownUsers) as $addedUser) { + \OCP\Util::emitHook('OC_User', 'post_addFromGroup', array('uid' => $addedUser, 'gid' => $group)); + \OCP\Util::writeLog('user_ldap', 'bgJ "updateGroups" – "'.$addedUser.'" added to "'.$group.'".', \OCP\Util::INFO); + $hasChanged = true; + } + if($hasChanged) { + $query->execute(array(serialize($actualUsers), $group)); + } + } + \OCP\Util::writeLog('user_ldap', 'bgJ "updateGroups" – FINISHED dealing with known Groups.', \OCP\Util::DEBUG); + } + + static private function handleCreatedGroups($createdGroups) { + \OCP\Util::writeLog('user_ldap', 'bgJ "updateGroups" – dealing with created Groups.', \OCP\Util::DEBUG); + $query = \OCP\DB::prepare(' + INSERT + INTO *PREFIX*ldap_group_members (owncloudname, owncloudusers) + VALUES (?, ?) + '); + foreach($createdGroups as $createdGroup) { + \OCP\Util::writeLog('user_ldap', 'bgJ "updateGroups" – new group "'.$createdGroup.'" found.', \OCP\Util::INFO); + $users = serialize(self::getGroupBE()->usersInGroup($createdGroup)); + $query->execute(array($createdGroup, $users)); + } + \OCP\Util::writeLog('user_ldap', 'bgJ "updateGroups" – FINISHED dealing with created Groups.', \OCP\Util::DEBUG); + } + + static private function handleRemovedGroups($removedGroups) { + \OCP\Util::writeLog('user_ldap', 'bgJ "updateGroups" – dealing with removed groups.', \OCP\Util::DEBUG); + $query = \OCP\DB::prepare(' + DELETE + FROM *PREFIX*ldap_group_members + WHERE owncloudname = ? + '); + foreach($removedGroups as $removedGroup) { + \OCP\Util::writeLog('user_ldap', 'bgJ "updateGroups" – group "'.$createdGroup.'" was removed.', \OCP\Util::INFO); + $query->execute(array($removedGroup)); + } + \OCP\Util::writeLog('user_ldap', 'bgJ "updateGroups" – FINISHED dealing with removed groups.', \OCP\Util::DEBUG); + } + + static private function getConnector() { + if(!is_null(self::$connector)) { + return self::$connector; + } + self::$connector = new \OCA\user_ldap\lib\Connection('user_ldap'); + return self::$connector; + } + + static private function getGroupBE() { + if(!is_null(self::$groupBE)) { + return self::$groupBE; + } + self::getConnector(); + self::$groupBE = new \OCA\user_ldap\GROUP_LDAP(); + self::$groupBE->setConnector(self::$connector); + + return self::$groupBE; + } + + static private function getKnownGroups() { + if(is_array(self::$groupsFromDB)) { + return self::$groupsFromDB; + } + $query = \OCP\DB::prepare(' + SELECT owncloudname, owncloudusers + FROM *PREFIX*ldap_group_members + '); + $result = $query->execute()->fetchAll(); + self::$groupsFromDB = array(); + foreach($result as $dataset) { + self::$groupsFromDB[$dataset['owncloudname']] = $dataset; + } + + return self::$groupsFromDB; + } +} \ No newline at end of file diff --git a/apps/user_ldap/lib_ldap.php b/apps/user_ldap/lib_ldap.php deleted file mode 100644 index 70b4664542..0000000000 --- a/apps/user_ldap/lib_ldap.php +++ /dev/null @@ -1,765 +0,0 @@ -. - * - */ - -define('LDAP_GROUP_MEMBER_ASSOC_ATTR','uniqueMember'); -define('LDAP_GROUP_DISPLAY_NAME_ATTR','cn'); - -//needed to unbind, because we use OC_LDAP only statically -class OC_LDAP_DESTRUCTOR { - public function __destruct() { - OC_LDAP::destruct(); - } -} - -class OC_LDAP { - static protected $ldapConnectionRes = false; - static protected $configured = false; - - //cached settings - static protected $ldapHost; - static protected $ldapPort; - static protected $ldapBase; - static protected $ldapBaseUsers; - static protected $ldapBaseGroups; - static protected $ldapAgentName; - static protected $ldapAgentPassword; - static protected $ldapTLS; - static protected $ldapNoCase; - static protected $ldapIgnoreNamingRules; - // user and group settings, that are needed in both backends - static protected $ldapUserDisplayName; - static protected $ldapUserFilter; - static protected $ldapGroupDisplayName; - static protected $ldapLoginFilter; - - static protected $__d; - - /** - * @brief initializes the LDAP backend - * @param $force read the config settings no matter what - * - * initializes the LDAP backend - */ - static public function init($force = false) { - if(is_null(self::$__d)) { - self::$__d = new OC_LDAP_DESTRUCTOR(); - } - self::readConfiguration($force); - self::establishConnection(); - } - - static public function destruct() { - @ldap_unbind(self::$ldapConnectionRes); - } - - /** - * @brief returns a read-only configuration value - * @param $key the name of the configuration value - * @returns the value on success, otherwise null - * - * returns a read-only configuration values - * - * we cannot work with getters, because it is a static class - */ - static public function conf($key) { - if(!self::$configured) { - self::init(); - } - - $availableProperties = array( - 'ldapUserDisplayName', - 'ldapGroupDisplayName', - 'ldapLoginFilter' - ); - - if(in_array($key, $availableProperties)) { - return self::$$key; - } - - return null; - } - - /** - * gives back the database table for the query - */ - static private function getMapTable($isUser) { - if($isUser) { - return '*PREFIX*ldap_user_mapping'; - } else { - return '*PREFIX*ldap_group_mapping'; - } - } - - /** - * @brief returns the LDAP DN for the given internal ownCloud name of the group - * @param $name the ownCloud name in question - * @returns string with the LDAP DN on success, otherwise false - * - * returns the LDAP DN for the given internal ownCloud name of the group - */ - static public function groupname2dn($name) { - return self::ocname2dn($name, false); - } - - /** - * @brief returns the LDAP DN for the given internal ownCloud name of the user - * @param $name the ownCloud name in question - * @returns string with the LDAP DN on success, otherwise false - * - * returns the LDAP DN for the given internal ownCloud name of the user - */ - static public function username2dn($name) { - $dn = self::ocname2dn($name, true); - if($dn) { - return $dn; - } else { - //fallback: user is not mapped - self::init(); - $filter = self::combineFilterWithAnd(array( - self::$ldapUserFilter, - self::$ldapUserDisplayName . '=' . $name, - )); - $result = self::searchUsers($filter, 'dn'); - if(isset($result[0]['dn'])) { - self::mapUser($result[0], $name); - return $result[0]; - } - } - - return false; - } - - static private function ocname2dn($name, $isUser) { - $table = self::getMapTable($isUser); - - $query = OCP\DB::prepare(' - SELECT `ldap_dn` - FROM `'.$table.'` - WHERE `owncloud_name` = ? - '); - - $record = $query->execute(array($name))->fetchOne(); - return $record; - } - - /** - * @brief returns the internal ownCloud name for the given LDAP DN of the group - * @param $dn the dn of the group object - * @param $ldapname optional, the display name of the object - * @returns string with with the name to use in ownCloud, false on DN outside of search DN - * - * returns the internal ownCloud name for the given LDAP DN of the group, false on DN outside of search DN or failure - */ - static public function dn2groupname($dn, $ldapname = null) { - if(strripos($dn, self::$ldapBaseGroups) !== (strlen($dn)-strlen(self::$ldapBaseGroups))) { - return false; - } - return self::dn2ocname($dn, $ldapname, false); - } - - /** - * @brief returns the internal ownCloud name for the given LDAP DN of the user - * @param $dn the dn of the user object - * @param $ldapname optional, the display name of the object - * @returns string with with the name to use in ownCloud - * - * returns the internal ownCloud name for the given LDAP DN of the user, false on DN outside of search DN or failure - */ - static public function dn2username($dn, $ldapname = null) { - if(strripos($dn, self::$ldapBaseUsers) !== (strlen($dn)-strlen(self::$ldapBaseUsers))) { - return false; - } - return self::dn2ocname($dn, $ldapname, true); - } - - static public function dn2ocname($dn, $ldapname = null, $isUser = true) { - $dn = self::sanitizeDN($dn); - $table = self::getMapTable($isUser); - if($isUser) { - $nameAttribute = self::conf('ldapUserDisplayName'); - } else { - $nameAttribute = self::conf('ldapGroupDisplayName'); - } - - $query = OCP\DB::prepare(' - SELECT `owncloud_name` - FROM `'.$table.'` - WHERE `ldap_dn` = ? - '); - - $component = $query->execute(array($dn))->fetchOne(); - if($component) { - return $component; - } - - if(is_null($ldapname)) { - $ldapname = self::readAttribute($dn, $nameAttribute); - //we do not accept empty usernames - if(!isset($ldapname[0]) && empty($ldapname[0])) { - OCP\Util::writeLog('user_ldap', 'No or empty name for '.$dn.'.', OCP\Util::INFO); - return false; - } - $ldapname = $ldapname[0]; - } - $ldapname = self::sanitizeUsername($ldapname); - - //a new user/group! Then let's try to add it. We're shooting into the blue with the user/group name, assuming that in most cases there will not be a conflict. Otherwise an error will occur and we will continue with our second shot. - if(self::mapComponent($dn, $ldapname, $isUser)) { - return $ldapname; - } - - //doh! There is a conflict. We need to distinguish between users/groups. Adding indexes is an idea, but not much of a help for the user. The DN is ugly, but for now the only reasonable way. But we transform it to a readable format and remove the first part to only give the path where this object is located. - $oc_name = self::alternateOwnCloudName($ldapname, $dn); - if(self::mapComponent($dn, $oc_name, $isUser)) { - return $oc_name; - } - - //if everything else did not help.. - OCP\Util::writeLog('user_ldap', 'Could not create unique ownCloud name for '.$dn.'.', OCP\Util::INFO); - } - - /** - * @brief gives back the user names as they are used ownClod internally - * @param $ldapGroups an array with the ldap Users result in style of array ( array ('dn' => foo, 'uid' => bar), ... ) - * @returns an array with the user names to use in ownCloud - * - * gives back the user names as they are used ownClod internally - */ - static public function ownCloudUserNames($ldapUsers) { - return self::ldap2ownCloudNames($ldapUsers, true); - } - - /** - * @brief gives back the group names as they are used ownClod internally - * @param $ldapGroups an array with the ldap Groups result in style of array ( array ('dn' => foo, 'cn' => bar), ... ) - * @returns an array with the group names to use in ownCloud - * - * gives back the group names as they are used ownClod internally - */ - static public function ownCloudGroupNames($ldapGroups) { - return self::ldap2ownCloudNames($ldapGroups, false); - } - - static private function ldap2ownCloudNames($ldapObjects, $isUsers) { - if($isUsers) { - $knownObjects = self::mappedUsers(); - $nameAttribute = self::conf('ldapUserDisplayName'); - } else { - $knownObjects = self::mappedGroups(); - $nameAttribute = self::conf('ldapGroupDisplayName'); - } - $ownCloudNames = array(); - - foreach($ldapObjects as $ldapObject) { - $key = self::recursiveArraySearch($knownObjects, $ldapObject['dn']); - - //everything is fine when we know the group - if($key !== false) { - $ownCloudNames[] = $knownObjects[$key]['owncloud_name']; - continue; - } - - //we do not take empty usernames - if(!isset($ldapObject[$nameAttribute]) || empty($ldapObject[$nameAttribute])) { - OCP\Util::writeLog('user_ldap', 'No or empty name for '.$ldapObject['dn'].', skipping.', OCP\Util::INFO); - continue; - } - - //a new group! Then let's try to add it. We're shooting into the blue with the group name, assuming that in most cases there will not be a conflict. But first make sure, that the display name contains only allowed characters. - $ocname = self::sanitizeUsername($ldapObject[$nameAttribute]); - if(self::mapComponent($ldapObject['dn'], $ocname, $isUsers)) { - $ownCloudNames[] = $ocname; - continue; - } - - //doh! There is a conflict. We need to distinguish between groups. Adding indexes is an idea, but not much of a help for the user. The DN is ugly, but for now the only reasonable way. But we transform it to a readable format and remove the first part to only give the path where this entry is located. - $ocname = self::alternateOwnCloudName($ocname, $ldapObject['dn']); - if(self::mapComponent($ldapObject['dn'], $ocname, $isUsers)) { - $ownCloudNames[] = $ocname; - continue; - } - - //if everything else did not help.. - OCP\Util::writeLog('user_ldap', 'Could not create unique ownCloud name for '.$ldapObject['dn'].', skipping.', OCP\Util::INFO); - } - return $ownCloudNames; - } - - /** - * @brief creates a hopefully unique name for owncloud based on the display name and the dn of the LDAP object - * @param $name the display name of the object - * @param $dn the dn of the object - * @returns string with with the name to use in ownCloud - * - * creates a hopefully unique name for owncloud based on the display name and the dn of the LDAP object - */ - static private function alternateOwnCloudName($name, $dn) { - $ufn = ldap_dn2ufn($dn); - $name = $name . '@' . trim(substr_replace($ufn, '', 0, strpos($ufn, ','))); - $name = self::sanitizeUsername($name); - return $name; - } - - /** - * @brief retrieves all known groups from the mappings table - * @returns array with the results - * - * retrieves all known groups from the mappings table - */ - static private function mappedGroups() { - return self::mappedComponents(false); - } - - /** - * @brief retrieves all known users from the mappings table - * @returns array with the results - * - * retrieves all known users from the mappings table - */ - static private function mappedUsers() { - return self::mappedComponents(true); - } - - static private function mappedComponents($isUsers) { - $table = self::getMapTable($isUsers); - - $query = OCP\DB::prepare(' - SELECT `ldap_dn`, `owncloud_name` - FROM `'. $table .'`' - ); - - return $query->execute()->fetchAll(); - } - - /** - * @brief inserts a new group into the mappings table - * @param $dn the record in question - * @param $ocname the name to use in ownCloud - * @returns true on success, false otherwise - * - * inserts a new group into the mappings table - */ - static private function mapGroup($dn, $ocname) { - return self::mapComponent($dn, $ocname, false); - } - - /** - * @brief inserts a new user into the mappings table - * @param $dn the record in question - * @param $ocname the name to use in ownCloud - * @returns true on success, false otherwise - * - * inserts a new user into the mappings table - */ - static private function mapUser($dn, $ocname) { - return self::mapComponent($dn, $ocname, true); - } - - /** - * @brief inserts a new user or group into the mappings table - * @param $dn the record in question - * @param $ocname the name to use in ownCloud - * @param $isUser is it a user or a group? - * @returns true on success, false otherwise - * - * inserts a new user or group into the mappings table - */ - static private function mapComponent($dn, $ocname, $isUser = true) { - $table = self::getMapTable($isUser); - $dn = self::sanitizeDN($dn); - - $sqlAdjustment = ''; - $dbtype = OCP\Config::getSystemValue('dbtype'); - if($dbtype == 'mysql') { - $sqlAdjustment = 'FROM `dual`'; - } - - $insert = OCP\DB::prepare(' - INSERT INTO `'.$table.'` (`ldap_dn`, `owncloud_name`) - SELECT ?,? - '.$sqlAdjustment.' - WHERE NOT EXISTS ( - SELECT 1 - FROM `'.$table.'` - WHERE `ldap_dn` = ? - OR `owncloud_name` = ? ) - '); - - $res = $insert->execute(array($dn, $ocname, $dn, $ocname)); - - if(OCP\DB::isError($res)) { - return false; - } - - $insRows = $res->numRows(); - - if($insRows == 0) { - return false; - } - - return true; - } - - static public function fetchListOfUsers($filter, $attr) { - return self::fetchList(OC_LDAP::searchUsers($filter, $attr), (count($attr) > 1)); - } - - static public function fetchListOfGroups($filter, $attr) { - return self::fetchList(OC_LDAP::searchGroups($filter, $attr), (count($attr) > 1)); - } - - static private function fetchList($list, $manyAttributes) { - if(is_array($list)) { - if($manyAttributes) { - return $list; - } else { - return array_unique($list, SORT_LOCALE_STRING); - } - } - - //error cause actually, maybe throw an exception in future. - return array(); - } - - /** - * @brief reads a given attribute for an LDAP record identified by a DN - * @param $dn the record in question - * @param $attr the attribute that shall be retrieved - * @returns the values in an array on success, false otherwise - * - * Reads an attribute from an LDAP entry - */ - static public function readAttribute($dn, $attr) { - $cr = self::getConnectionResource(); - if(!is_resource($cr)) { - //LDAP not available - return false; - } - $rr = ldap_read($cr, $dn, 'objectClass=*', array($attr)); - $er = ldap_first_entry($cr, $rr); - //LDAP attributes are not case sensitive - $result = array_change_key_case(ldap_get_attributes($cr, $er)); - $attr = strtolower($attr); - - if(isset($result[$attr]) && $result[$attr]['count'] > 0){ - $values = array(); - for($i=0;$i<$result[$attr]['count'];$i++) { - $values[] = self::resemblesDN($attr) ? self::sanitizeDN($result[$attr][$i]) : $result[$attr][$i]; - } - return $values; - } - return false; - } - - /** - * @brief executes an LDAP search, optimized for Users - * @param $filter the LDAP filter for the search - * @param $attr optional, when a certain attribute shall be filtered out - * @returns array with the search result - * - * Executes an LDAP search - */ - static public function searchUsers($filter, $attr = null) { - self::init(); - return self::search($filter, self::$ldapBaseUsers, $attr); - } - - /** - * @brief executes an LDAP search, optimized for Groups - * @param $filter the LDAP filter for the search - * @param $attr optional, when a certain attribute shall be filtered out - * @returns array with the search result - * - * Executes an LDAP search - */ - static public function searchGroups($filter, $attr = null) { - self::init(); - return self::search($filter, self::$ldapBaseGroups, $attr); - } - - /** - * @brief executes an LDAP search - * @param $filter the LDAP filter for the search - * @param $base the LDAP subtree that shall be searched - * @param $attr optional, when a certain attribute shall be filtered out - * @returns array with the search result - * - * Executes an LDAP search - */ - static private function search($filter, $base, $attr = null) { - if(!is_null($attr) && !is_array($attr)) { - $attr = array(strtolower($attr)); - } - $cr = self::getConnectionResource(); - if(!is_resource($cr)) { - //LDAP not available - return array(); - } - $sr = @ldap_search(self::getConnectionResource(), $base, $filter, $attr); - $findings = @ldap_get_entries(self::getConnectionResource(), $sr ); - // if we're here, probably no connection ressource is returned. - // to make ownCloud behave nicely, we simply give back an empty array. - if(is_null($findings)) { - return array(); - } - - if(!is_null($attr)) { - $selection = array(); - $multiarray = false; - if(count($attr) > 1) { - $multiarray = true; - $i = 0; - } - foreach($findings as $item) { - if(!is_array($item)) { - continue; - } - $item = array_change_key_case($item); - - if($multiarray) { - foreach($attr as $key) { - $key = strtolower($key); - if(isset($item[$key])) { - if($key != 'dn'){ - $selection[$i][$key] = self::resemblesDN($key) ? self::sanitizeDN($item[$key][0]) : $item[$key][0]; - } else { - $selection[$i][$key] = self::sanitizeDN($item[$key]); - } - } - - } - $i++; - } else { - //tribute to case insensitivity - $key = strtolower($attr[0]); - - if(isset($item[$key])) { - if(self::resemblesDN($key)) { - $selection[] = self::sanitizeDN($item[$key]); - } else { - $selection[] = $item[$key]; - } - } - } - - } - return $selection; - } - - return $findings; - } - - static private function resemblesDN($attr) { - $resemblingAttributes = array( - 'dn', - 'uniquemember', - 'member' - ); - return in_array($attr, $resemblingAttributes); - } - - static private function sanitizeDN($dn) { - //OID sometimes gives back DNs with whitespace after the comma a la "uid=foo, cn=bar, dn=..." We need to tackle this! - $dn = preg_replace('/([^\\\]),(\s+)/','\1,',$dn); - - //make comparisons and everything work - $dn = strtolower($dn); - - return $dn; - } - - static private function sanitizeUsername($name) { - if(self::$ldapIgnoreNamingRules) { - return $name; - } - - //REPLACEMENTS - $name = str_replace(' ', '_', $name); - - //every remaining unallowed characters will be removed - $name = preg_replace('/[^a-zA-Z0-9_.@-]/', '', $name); - - return $name; - } - - /** - * @brief combines the input filters with AND - * @param $filters array, the filters to connect - * @returns the combined filter - * - * Combines Filter arguments with AND - */ - static public function combineFilterWithAnd($filters) { - return self::combineFilter($filters,'&'); - } - - /** - * @brief combines the input filters with AND - * @param $filters array, the filters to connect - * @returns the combined filter - * - * Combines Filter arguments with AND - */ - static public function combineFilterWithOr($filters) { - return self::combineFilter($filters,'|'); - } - - /** - * @brief combines the input filters with given operator - * @param $filters array, the filters to connect - * @param $operator either & or | - * @returns the combined filter - * - * Combines Filter arguments with AND - */ - static private function combineFilter($filters, $operator) { - $combinedFilter = '('.$operator; - foreach($filters as $filter) { - if($filter[0] != '(') { - $filter = '('.$filter.')'; - } - $combinedFilter.=$filter; - } - $combinedFilter.=')'; - return $combinedFilter; - } - - /** - * Returns the LDAP handler - */ - static private function getConnectionResource() { - if(!self::$ldapConnectionRes) { - self::init(); - } - if(is_null(self::$ldapConnectionRes)) { - OCP\Util::writeLog('ldap', 'Connection could not be established', OCP\Util::INFO); - } - return self::$ldapConnectionRes; - } - - /** - * Caches the general LDAP configuration. - */ - static private function readConfiguration($force = false) { - if(!self::$configured || $force) { - self::$ldapHost = OCP\Config::getAppValue('user_ldap', 'ldap_host', ''); - self::$ldapPort = OCP\Config::getAppValue('user_ldap', 'ldap_port', 389); - self::$ldapAgentName = OCP\Config::getAppValue('user_ldap', 'ldap_dn',''); - self::$ldapAgentPassword = base64_decode(OCP\Config::getAppValue('user_ldap', 'ldap_agent_password','')); - self::$ldapBase = self::sanitizeDN(OCP\Config::getAppValue('user_ldap', 'ldap_base', '')); - self::$ldapBaseUsers = self::sanitizeDN(OCP\Config::getAppValue('user_ldap', 'ldap_base_users',self::$ldapBase)); - self::$ldapBaseGroups = self::sanitizeDN(OCP\Config::getAppValue('user_ldap', 'ldap_base_groups', self::$ldapBase)); - self::$ldapTLS = OCP\Config::getAppValue('user_ldap', 'ldap_tls',0); - self::$ldapNoCase = OCP\Config::getAppValue('user_ldap', 'ldap_nocase', 0); - self::$ldapUserDisplayName = strtolower(OCP\Config::getAppValue('user_ldap', 'ldap_display_name', 'uid')); - self::$ldapUserFilter = OCP\Config::getAppValue('user_ldap', 'ldap_userlist_filter','objectClass=person'); - self::$ldapLoginFilter = OCP\Config::getAppValue('user_ldap', 'ldap_login_filter', '(uid=%uid)'); - self::$ldapGroupDisplayName = strtolower(OCP\Config::getAppValue('user_ldap', 'ldap_group_display_name', LDAP_GROUP_DISPLAY_NAME_ATTR)); - self::$ldapIgnoreNamingRules = OCP\Config::getSystemValue('ldapIgnoreNamingRules', false); - - if(empty(self::$ldapBaseUsers)) { - OCP\Util::writeLog('ldap', 'Base for Users is empty, using Base DN', OCP\Util::INFO); - self::$ldapBaseUsers = self::$ldapBase; - } - if(empty(self::$ldapBaseGroups)) { - OCP\Util::writeLog('ldap', 'Base for Groups is empty, using Base DN', OCP\Util::INFO); - self::$ldapBaseGroups = self::$ldapBase; - } - - if( - !empty(self::$ldapHost) - && !empty(self::$ldapPort) - && ( - (!empty(self::$ldapAgentName) && !empty(self::$ldapAgentPassword)) - || ( empty(self::$ldapAgentName) && empty(self::$ldapAgentPassword)) - ) - && !empty(self::$ldapBase) - && !empty(self::$ldapUserDisplayName) - ) - { - self::$configured = true; - } - } - } - - /** - * Connects and Binds to LDAP - */ - static private function establishConnection() { - static $phpLDAPinstalled = true; - if(!$phpLDAPinstalled) { - return false; - } - if(!self::$configured) { - OCP\Util::writeLog('ldap', 'Configuration is invalid, cannot connect', OCP\Util::INFO); - return false; - } - if(!self::$ldapConnectionRes) { - //check if php-ldap is installed - if(!function_exists('ldap_connect')) { - $phpLDAPinstalled = false; - OCP\Util::writeLog('user_ldap', 'function ldap_connect is not available. Make sure that the PHP ldap module is installed.', OCP\Util::ERROR); - - return false; - } - self::$ldapConnectionRes = ldap_connect(self::$ldapHost, self::$ldapPort); - if(ldap_set_option(self::$ldapConnectionRes, LDAP_OPT_PROTOCOL_VERSION, 3)) { - if(ldap_set_option(self::$ldapConnectionRes, LDAP_OPT_REFERRALS, 0)) { - if(self::$ldapTLS) { - ldap_start_tls(self::$ldapConnectionRes); - } - } - } - - $ldapLogin = @ldap_bind(self::$ldapConnectionRes, self::$ldapAgentName, self::$ldapAgentPassword ); - if(!$ldapLogin) { - OCP\Util::writeLog('ldap', 'Bind failed: ' . ldap_errno(self::$ldapConnectionRes) . ': ' . ldap_error(self::$ldapConnectionRes), OCP\Util::ERROR); - return false; - } - } - } - - static public function areCredentialsValid($name, $password) { - return @ldap_bind(self::getConnectionResource(), $name, $password); - } - - /** - * taken from http://www.php.net/manual/en/function.array-search.php#97645 - * TODO: move somewhere, where its better placed since it is not LDAP specific. OC_Helper maybe? - */ - static public function recursiveArraySearch($haystack, $needle, $index = null) { - $aIt = new RecursiveArrayIterator($haystack); - $it = new RecursiveIteratorIterator($aIt); - - while($it->valid()) { - if (((isset($index) AND ($it->key() == $index)) OR (!isset($index))) AND ($it->current() == $needle)) { - return $aIt->key(); - } - - $it->next(); - } - - return false; - } - - } diff --git a/apps/user_ldap/settings.php b/apps/user_ldap/settings.php index 0c29e70b5a..39aa329626 100644 --- a/apps/user_ldap/settings.php +++ b/apps/user_ldap/settings.php @@ -20,34 +20,43 @@ * License along with this library. If not, see . * */ -$params = array('ldap_host', 'ldap_port', 'ldap_dn', 'ldap_agent_password', 'ldap_base', 'ldap_base_users', 'ldap_base_groups', 'ldap_userlist_filter', 'ldap_login_filter', 'ldap_group_filter', 'ldap_display_name', 'ldap_group_display_name', 'ldap_tls', 'ldap_nocase', 'ldap_quota_def', 'ldap_quota_attr', 'ldap_email_attr', 'ldap_group_member_assoc_attribute'); +$params = array('ldap_host', 'ldap_port', 'ldap_dn', 'ldap_agent_password', 'ldap_base', 'ldap_base_users', 'ldap_base_groups', 'ldap_userlist_filter', 'ldap_login_filter', 'ldap_group_filter', 'ldap_display_name', 'ldap_group_display_name', 'ldap_tls', 'ldap_turn_off_cert_check', 'ldap_nocase', 'ldap_quota_def', 'ldap_quota_attr', 'ldap_email_attr', 'ldap_group_member_assoc_attribute', 'ldap_cache_ttl'); OCP\Util::addscript('user_ldap', 'settings'); +OCP\Util::addstyle('user_ldap', 'settings'); if ($_POST) { foreach($params as $param){ if(isset($_POST[$param])){ if('ldap_agent_password' == $param) { OCP\Config::setAppValue('user_ldap', $param, base64_encode($_POST[$param])); + } elseif('ldap_cache_ttl' == $param) { + if(OCP\Config::getAppValue('user_ldap', $param,'') != $_POST[$param]) { + $ldap = new \OCA\user_ldap\lib\Connection('user_ldap'); + $ldap->clearCache(); + OCP\Config::setAppValue('user_ldap', $param, $_POST[$param]); + } } else { OCP\Config::setAppValue('user_ldap', $param, $_POST[$param]); } } elseif('ldap_tls' == $param) { // unchecked checkboxes are not included in the post paramters - OCP\Config::setAppValue('user_ldap', $param, 0); + OCP\Config::setAppValue('user_ldap', $param, 0); } elseif('ldap_nocase' == $param) { OCP\Config::setAppValue('user_ldap', $param, 0); } - + elseif('ldap_turn_off_cert_check' == $param) { + OCP\Config::setAppValue('user_ldap', $param, 0); + } } } // fill template $tmpl = new OCP\Template( 'user_ldap', 'settings'); foreach($params as $param){ - $value = htmlentities(OCP\Config::getAppValue('user_ldap', $param,'')); + $value = OCP\Config::getAppValue('user_ldap', $param,''); $tmpl->assign($param, $value); } @@ -57,5 +66,6 @@ $tmpl->assign( 'ldap_display_name', OCP\Config::getAppValue('user_ldap', 'ldap_d $tmpl->assign( 'ldap_group_display_name', OCP\Config::getAppValue('user_ldap', 'ldap_group_display_name', 'cn')); $tmpl->assign( 'ldap_group_member_assoc_attribute', OCP\Config::getAppValue('user_ldap', 'ldap_group_member_assoc_attribute', 'uniqueMember')); $tmpl->assign( 'ldap_agent_password', base64_decode(OCP\Config::getAppValue('user_ldap', 'ldap_agent_password'))); +$tmpl->assign( 'ldap_cache_ttl', OCP\Config::getAppValue('user_ldap', 'ldap_cache_ttl', '600')); return $tmpl->fetchPage(); diff --git a/apps/user_ldap/templates/settings.php b/apps/user_ldap/templates/settings.php index 31f453b5a5..861c9ba605 100644 --- a/apps/user_ldap/templates/settings.php +++ b/apps/user_ldap/templates/settings.php @@ -5,29 +5,30 @@
      • Advanced
      • -

        -

        - - t('Leave both empty for anonymous bind for search, then bind with users credentials.');?>

        -

        t('use %%uid placeholder, e.g. uid=%%uid');?>

        -

        t('without any placeholder, e.g. "objectClass=person".');?>

        -

        t('without any placeholder, e.g. "objectClass=posixGroup".');?>

        +

        +

        +

        +

        +


        t('use %%uid placeholder, e.g. "uid=%%uid"');?>

        +


        t('without any placeholder, e.g. "objectClass=person".');?>

        +


        t('without any placeholder, e.g. "objectClass=posixGroup".');?>

        -

        >

        -

        >

        -

        - t('Currently the display name field needs to be the same you matched %%uid against in the filter above, because ownCloud doesn\'t distinguish between user id and user name.');?>

        -

        -

        - bytes

        -

        +

        title="t('Do not use it for SSL connections, it will fail.');?>" />

        +

        >

        +

        >
        t('Not recommended, use for testing only.');?>

        +

        +

        +

        +

        +

        +

        - t('Help');?> + t('Help');?> diff --git a/apps/user_ldap/tests/group_ldap.php b/apps/user_ldap/tests/group_ldap.php index 2be6b46fb2..106459580f 100644 --- a/apps/user_ldap/tests/group_ldap.php +++ b/apps/user_ldap/tests/group_ldap.php @@ -26,8 +26,8 @@ class Test_Group_Ldap extends UnitTestCase { } function testSingleBackend(){ - OC_Group::useBackend(new OC_GROUP_LDAP()); - $group_ldap = new OC_GROUP_LDAP(); + OC_Group::useBackend(new OCA\user_ldap\GROUP_LDAP()); + $group_ldap = new OCA\user_ldap\GROUP_LDAP(); $this->assertIsA(OC_Group::getGroups(),gettype(array())); $this->assertIsA($group_ldap->getGroups(),gettype(array())); diff --git a/apps/user_ldap/user_ldap.php b/apps/user_ldap/user_ldap.php index da99e167fd..a97df7b4fd 100644 --- a/apps/user_ldap/user_ldap.php +++ b/apps/user_ldap/user_ldap.php @@ -23,53 +23,36 @@ * */ -class OC_USER_LDAP extends OC_User_Backend { +namespace OCA\user_ldap; - // cached settings - protected $ldapUserFilter; - protected $ldapQuotaAttribute; - protected $ldapQuotaDefault; - protected $ldapEmailAttribute; - - // will be retrieved from LDAP server - protected $ldap_dc = false; - - // cache getUsers() - protected $_users = null; - - public function __construct() { - $this->ldapUserFilter = OCP\Config::getAppValue('user_ldap', 'ldap_userlist_filter', '(objectClass=posixAccount)'); - $this->ldapQuotaAttribute = OCP\Config::getAppValue('user_ldap', 'ldap_quota_attr', ''); - $this->ldapQuotaDefault = OCP\Config::getAppValue('user_ldap', 'ldap_quota_def', ''); - $this->ldapEmailAttribute = OCP\Config::getAppValue('user_ldap', 'ldap_email_attr', ''); - } +class USER_LDAP extends lib\Access implements \OCP\UserInterface { private function updateQuota($dn) { $quota = null; - if(!empty($this->ldapQuotaDefault)) { - $quota = $this->ldapQuotaDefault; + if(!empty($this->connection->ldapQuotaDefault)) { + $quota = $this->connection->ldapQuotaDefault; } - if(!empty($this->ldapQuotaAttribute)) { - $aQuota = OC_LDAP::readAttribute($dn, $this->ldapQuotaAttribute); + if(!empty($this->connection->ldapQuotaAttribute)) { + $aQuota = $this->readAttribute($dn, $this->connection->ldapQuotaAttribute); if($aQuota && (count($aQuota) > 0)) { $quota = $aQuota[0]; } } if(!is_null($quota)) { - OCP\Config::setUserValue(OC_LDAP::dn2username($dn), 'files', 'quota', OCP\Util::computerFileSize($quota)); + \OCP\Config::setUserValue($this->dn2username($dn), 'files', 'quota', \OCP\Util::computerFileSize($quota)); } } private function updateEmail($dn) { $email = null; - if(!empty($this->ldapEmailAttribute)) { - $aEmail = OC_LDAP::readAttribute($dn, $this->ldapEmailAttribute); + if(!empty($this->connection->ldapEmailAttribute)) { + $aEmail = $this->readAttribute($dn, $this->connection->ldapEmailAttribute); if($aEmail && (count($aEmail) > 0)) { $email = $aEmail[0]; } - if(!is_null($email)){ - OCP\Config::setUserValue(OC_LDAP::dn2username($dn), 'settings', 'email', $email); + if(!is_null($email)) { + \OCP\Config::setUserValue($this->dn2username($dn), 'settings', 'email', $email); } } } @@ -84,26 +67,27 @@ class OC_USER_LDAP extends OC_User_Backend { */ public function checkPassword($uid, $password){ //find out dn of the user name - $filter = str_replace('%uid', $uid, OC_LDAP::conf('ldapLoginFilter')); - $ldap_users = OC_LDAP::fetchListOfUsers($filter, 'dn'); + $filter = \OCP\Util::mb_str_replace('%uid', $uid, $this->connection->ldapLoginFilter, 'UTF-8'); + $ldap_users = $this->fetchListOfUsers($filter, 'dn'); if(count($ldap_users) < 1) { return false; } $dn = $ldap_users[0]; //are the credentials OK? - if(!OC_LDAP::areCredentialsValid($dn, $password)) { + if(!$this->areCredentialsValid($dn, $password)) { return false; } //do we have a username for him/her? - $ocname = OC_LDAP::dn2username($dn); + $ocname = $this->dn2username($dn); if($ocname){ //update some settings, if necessary $this->updateQuota($dn); $this->updateEmail($dn); + //give back the display name return $ocname; } @@ -116,12 +100,25 @@ class OC_USER_LDAP extends OC_User_Backend { * * Get a list of all users. */ - public function getUsers(){ - if(is_null($this->_users)) { - $ldap_users = OC_LDAP::fetchListOfUsers($this->ldapUserFilter, array(OC_LDAP::conf('ldapUserDisplayName'), 'dn')); - $this->_users = OC_LDAP::ownCloudUserNames($ldap_users); + public function getUsers($search = '', $limit = 10, $offset = 0){ + $ldap_users = $this->connection->getFromCache('getUsers'); + if(is_null($ldap_users)) { + $ldap_users = $this->fetchListOfUsers($this->connection->ldapUserFilter, array($this->connection->ldapUserDisplayName, 'dn')); + $ldap_users = $this->ownCloudUserNames($ldap_users); + $this->connection->writeToCache('getUsers', $ldap_users); } - return $this->_users; + $this->userSearch = $search; + if(!empty($this->userSearch)) { + $ldap_users = array_filter($ldap_users, array($this, 'userMatchesFilter')); + } + if($limit = -1) { + $limit = null; + } + return array_slice($ldap_users, $offset, $limit); + } + + public function userMatchesFilter($user) { + return (strripos($user, $this->userSearch) !== false); } /** @@ -130,19 +127,49 @@ class OC_USER_LDAP extends OC_User_Backend { * @return boolean */ public function userExists($uid){ + if($this->connection->isCached('userExists'.$uid)) { + return $this->connection->getFromCache('userExists'.$uid); + } + //getting dn, if false the user does not exist. If dn, he may be mapped only, requires more checking. - $dn = OC_LDAP::username2dn($uid); + $dn = $this->username2dn($uid); if(!$dn) { + $this->connection->writeToCache('userExists'.$uid, false); return false; } - //if user really still exists, we will be able to read his cn - $cn = OC_LDAP::readAttribute($dn, 'cn'); - if(!$cn || empty($cn)) { + //if user really still exists, we will be able to read his objectclass + $objcs = $this->readAttribute($dn, 'objectclass'); + if(!$objcs || empty($objcs)) { + $this->connection->writeToCache('userExists'.$uid, false); return false; } + $this->connection->writeToCache('userExists'.$uid, true); return true; } + /** + * @brief delete a user + * @param $uid The username of the user to delete + * @returns true/false + * + * Deletes a user + */ + public function deleteUser($uid) { + return false; + } + + /** + * @brief Check if backend implements actions + * @param $actions bitwise-or'ed actions + * @returns boolean + * + * Returns the supported actions as int to be + * compared with OC_USER_BACKEND_CREATE_USER etc. + */ + public function implementsActions($actions) { + return (bool)(OC_USER_BACKEND_CHECK_PASSWORD & $actions); + } + } \ No newline at end of file diff --git a/apps/user_migrate/appinfo/app.php b/apps/user_migrate/appinfo/app.php index 9d314b59ce..366c400493 100644 --- a/apps/user_migrate/appinfo/app.php +++ b/apps/user_migrate/appinfo/app.php @@ -31,4 +31,3 @@ $entry = array( 'href' => OCP\Util::linkTo( "user_migrate", "admin.php" ), 'name' => 'Import' ); -?> diff --git a/apps/user_migrate/l10n/.gitkeep b/apps/user_migrate/l10n/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apps/user_migrate/l10n/ca.php b/apps/user_migrate/l10n/ca.php new file mode 100644 index 0000000000..6e423c01b0 --- /dev/null +++ b/apps/user_migrate/l10n/ca.php @@ -0,0 +1,10 @@ + "Exporta", +"Something went wrong while the export file was being generated" => "Alguna cosa ha anat malament en generar el fitxer d'exportació", +"An error has occurred" => "S'ha produït un error", +"Export your user account" => "Exportar el compte d'usuari", +"This will create a compressed file that contains your ownCloud account." => "Això crearà un fitxer comprimit que conté el vostre compte ownCloud.", +"Import user account" => "Importar el compte d'usuari", +"ownCloud User Zip" => "zip d'usuari ownCloud", +"Import" => "Importa" +); diff --git a/apps/user_migrate/l10n/cs_CZ.php b/apps/user_migrate/l10n/cs_CZ.php new file mode 100644 index 0000000000..81a5a45d97 --- /dev/null +++ b/apps/user_migrate/l10n/cs_CZ.php @@ -0,0 +1,10 @@ + "Export", +"Something went wrong while the export file was being generated" => "Během vytváření souboru exportu došlo k chybě", +"An error has occurred" => "Nastala chyba", +"Export your user account" => "Export Vašeho uživatelského účtu", +"This will create a compressed file that contains your ownCloud account." => "Bude vytvořen komprimovaný soubor, obsahující Váš ownCloud účet.", +"Import user account" => "Import uživatelského účtu", +"ownCloud User Zip" => "Zip soubor uživatele ownCloud", +"Import" => "Import" +); diff --git a/apps/user_migrate/l10n/de.php b/apps/user_migrate/l10n/de.php new file mode 100644 index 0000000000..928a211dd4 --- /dev/null +++ b/apps/user_migrate/l10n/de.php @@ -0,0 +1,10 @@ + "Export", +"Something went wrong while the export file was being generated" => "Beim Export der Datei ist etwas schiefgegangen.", +"An error has occurred" => "Es ist ein Fehler aufgetreten.", +"Export your user account" => "Ihr Konto exportieren", +"This will create a compressed file that contains your ownCloud account." => "Eine komprimierte Datei wird erzeugt, die Ihr ownCloud-Konto enthält.", +"Import user account" => "Konto importieren", +"ownCloud User Zip" => "Zip-Archiv mit Benutzerdaten", +"Import" => "Importieren" +); diff --git a/apps/user_migrate/l10n/el.php b/apps/user_migrate/l10n/el.php new file mode 100644 index 0000000000..5b57e69a63 --- /dev/null +++ b/apps/user_migrate/l10n/el.php @@ -0,0 +1,8 @@ + "Εξαγωγή", +"An error has occurred" => "Παρουσιάστηκε σφάλμα", +"Export your user account" => "Εξαγωγή του λογαριασμού χρήστη σας", +"This will create a compressed file that contains your ownCloud account." => "Αυτό θα δημιουργήσει ένα συμπιεσμένο αρχείο που θα περιέχει τον λογαριασμό σας ownCloud.", +"Import user account" => "Εισαγωγή λογαριασμού χρήστη", +"Import" => "Εισαγωγή" +); diff --git a/apps/user_migrate/l10n/eo.php b/apps/user_migrate/l10n/eo.php new file mode 100644 index 0000000000..ca91806169 --- /dev/null +++ b/apps/user_migrate/l10n/eo.php @@ -0,0 +1,10 @@ + "Malenporti", +"Something went wrong while the export file was being generated" => "Io malsukcesis dum la enportota dosiero generiĝis", +"An error has occurred" => "Eraro okazis", +"Export your user account" => "Malenporti vian uzantokonton", +"This will create a compressed file that contains your ownCloud account." => "Ĉi tio kreos densigitan dosieron, kiu enhavas vian konton de ownCloud.", +"Import user account" => "Enporti uzantokonton", +"ownCloud User Zip" => "ZIP-dosiero de uzanto de ownCloud", +"Import" => "Enporti" +); diff --git a/apps/user_migrate/l10n/es.php b/apps/user_migrate/l10n/es.php new file mode 100644 index 0000000000..6a0551fe23 --- /dev/null +++ b/apps/user_migrate/l10n/es.php @@ -0,0 +1,5 @@ + "Exportar", +"ownCloud User Zip" => "Zip de usuario de ownCloud", +"Import" => "Importar" +); diff --git a/apps/user_migrate/l10n/fi_FI.php b/apps/user_migrate/l10n/fi_FI.php new file mode 100644 index 0000000000..2616bee6f2 --- /dev/null +++ b/apps/user_migrate/l10n/fi_FI.php @@ -0,0 +1,9 @@ + "Vie", +"Something went wrong while the export file was being generated" => "Jokin meni pieleen vientiä suorittaessa", +"An error has occurred" => "Tapahtui virhe", +"Export your user account" => "Vie käyttäjätilisi", +"This will create a compressed file that contains your ownCloud account." => "Tämä luo ownCloud-käyttäjätilisi sisältävän pakatun tiedoston.", +"Import user account" => "Tuo käyttäjätili", +"Import" => "Tuo" +); diff --git a/apps/user_migrate/l10n/fr.php b/apps/user_migrate/l10n/fr.php new file mode 100644 index 0000000000..70ab2f8f22 --- /dev/null +++ b/apps/user_migrate/l10n/fr.php @@ -0,0 +1,10 @@ + "Exporter", +"Something went wrong while the export file was being generated" => "Une erreur s'est produite pendant la génération du fichier d'export", +"An error has occurred" => "Une erreur s'est produite", +"Export your user account" => "Exportez votre compte utilisateur", +"This will create a compressed file that contains your ownCloud account." => "Cette action va créer une archive compressée qui contiendra les données de votre compte ownCloud.", +"Import user account" => "Importer un compte utilisateur", +"ownCloud User Zip" => "Archive Zip de l'utilisateur", +"Import" => "Importer" +); diff --git a/apps/user_migrate/l10n/it.php b/apps/user_migrate/l10n/it.php new file mode 100644 index 0000000000..f3eeb15dab --- /dev/null +++ b/apps/user_migrate/l10n/it.php @@ -0,0 +1,10 @@ + "Esporta", +"Something went wrong while the export file was being generated" => "Si è verificato un errore durante la creazione del file di esportazione", +"An error has occurred" => "Si è verificato un errore", +"Export your user account" => "Esporta il tuo account utente", +"This will create a compressed file that contains your ownCloud account." => "Questa operazione creerà un file compresso che contiene il tuo account ownCloud.", +"Import user account" => "Importa account utente", +"ownCloud User Zip" => "Zip account utente", +"Import" => "Importa" +); diff --git a/apps/user_migrate/l10n/ja_JP.php b/apps/user_migrate/l10n/ja_JP.php new file mode 100644 index 0000000000..86c66e6319 --- /dev/null +++ b/apps/user_migrate/l10n/ja_JP.php @@ -0,0 +1,10 @@ + "エクスポート", +"Something went wrong while the export file was being generated" => "エクスポートファイルの生成時に何か不具合が発生しました。", +"An error has occurred" => "エラーが発生しました", +"Export your user account" => "ユーザアカウントのエクスポート", +"This will create a compressed file that contains your ownCloud account." => "あなたのownCloudアカウントを含む圧縮ファイルを生成します。", +"Import user account" => "ユーザアカウントをインポート", +"ownCloud User Zip" => "ownCloudユーザZip", +"Import" => "インポート" +); diff --git a/apps/user_migrate/l10n/lt_LT.php b/apps/user_migrate/l10n/lt_LT.php new file mode 100644 index 0000000000..20add8037b --- /dev/null +++ b/apps/user_migrate/l10n/lt_LT.php @@ -0,0 +1,10 @@ + "Eksportuoti", +"Something went wrong while the export file was being generated" => "Įvyko klaida kuriant eksportuojamą failą", +"An error has occurred" => "Įvyko klaida", +"Export your user account" => "Eksportuoti jūsų vartotojo paskyrą", +"This will create a compressed file that contains your ownCloud account." => "Bus sukurtas suglaudintas failas su jūsų ownCloud vartotojo paskyra.", +"Import user account" => "Importuoti vartotojo paskyrą", +"ownCloud User Zip" => "ownCloud vartotojo paskyros Zip archyvas", +"Import" => "Importuoti" +); diff --git a/apps/user_migrate/l10n/pl.php b/apps/user_migrate/l10n/pl.php new file mode 100644 index 0000000000..c3a6332578 --- /dev/null +++ b/apps/user_migrate/l10n/pl.php @@ -0,0 +1,10 @@ + "Eksport", +"Something went wrong while the export file was being generated" => "Coś poszło źle, podczas generowania pliku eksportu", +"An error has occurred" => "Wystąpił błąd", +"Export your user account" => "Eksportuj konto użytkownika", +"This will create a compressed file that contains your ownCloud account." => "Spowoduje to utworzenie pliku skompresowanego, który zawiera konto ownCloud.", +"Import user account" => "Importuj konto użytkownika", +"ownCloud User Zip" => "paczka Zip użytkownika ownCloud", +"Import" => "Importuj" +); diff --git a/apps/user_migrate/l10n/sl.php b/apps/user_migrate/l10n/sl.php new file mode 100644 index 0000000000..c14bca0f70 --- /dev/null +++ b/apps/user_migrate/l10n/sl.php @@ -0,0 +1,10 @@ + "Izvozi", +"Something went wrong while the export file was being generated" => "Med ustvarjanjem datoteke za izvoz je prišlo do napake", +"An error has occurred" => "Prišlo je do napake", +"Export your user account" => "Izvozi vaš uporabniški račun", +"This will create a compressed file that contains your ownCloud account." => "Ustvarjena bo stisnjena datoteka z vašim ownCloud računom.", +"Import user account" => "Uvozi uporabniški račun", +"ownCloud User Zip" => "Zip datoteka ownCloud uporabnika", +"Import" => "Uvozi" +); diff --git a/apps/user_migrate/l10n/sv.php b/apps/user_migrate/l10n/sv.php new file mode 100644 index 0000000000..98e649632b --- /dev/null +++ b/apps/user_migrate/l10n/sv.php @@ -0,0 +1,10 @@ + "Exportera", +"Something went wrong while the export file was being generated" => "Något gick fel när exportfilen skulle genereras", +"An error has occurred" => "Ett fel har uppstått", +"Export your user account" => "Exportera ditt användarkonto", +"This will create a compressed file that contains your ownCloud account." => "Detta vill skapa en komprimerad fil som innehåller ditt ownCloud-konto.", +"Import user account" => "Importera ett användarkonto", +"ownCloud User Zip" => "ownCloud Zip-fil", +"Import" => "Importera" +); diff --git a/apps/user_migrate/l10n/th_TH.php b/apps/user_migrate/l10n/th_TH.php new file mode 100644 index 0000000000..d2c36bb4ed --- /dev/null +++ b/apps/user_migrate/l10n/th_TH.php @@ -0,0 +1,10 @@ + "ส่งออก", +"Something went wrong while the export file was being generated" => "เกิดข้อผิดพลาดบางประการในระหว่างการส่งออกไฟล์", +"An error has occurred" => "เกิดข้อผิดพลาดบางประการ", +"Export your user account" => "ส่งออกบัญชีผู้ใช้งานของคุณ", +"This will create a compressed file that contains your ownCloud account." => "ส่วนนี้จะเป็นการสร้างไฟล์บีบอัดที่บรรจุข้อมูลบัญชีผู้ใช้งาน ownCloud ของคุณ", +"Import user account" => "นำเข้าบัญชีผู้ใช้งาน", +"ownCloud User Zip" => "ไฟล์ Zip ผู้ใช้งาน ownCloud", +"Import" => "นำเข้า" +); diff --git a/apps/user_migrate/templates/settings.php b/apps/user_migrate/templates/settings.php index 1718abe9e0..d6f3e2f8f6 100644 --- a/apps/user_migrate/templates/settings.php +++ b/apps/user_migrate/templates/settings.php @@ -2,7 +2,7 @@ t('Export your user account');?>

        t('This will create a compressed file that contains your ownCloud account.');?>

        - +
        @@ -12,7 +12,7 @@ t('Import user account');?>

        -

        +

        diff --git a/apps/user_openid/appinfo/app.php b/apps/user_openid/appinfo/app.php index c683254101..fe57b189fa 100644 --- a/apps/user_openid/appinfo/app.php +++ b/apps/user_openid/appinfo/app.php @@ -27,7 +27,7 @@ OC_User::useBackend('openid'); //check for results from openid requests if(isset($_GET['openid_mode']) and $_GET['openid_mode'] == 'id_res'){ OCP\Util::writeLog('user_openid','openid retured',OCP\Util::DEBUG); - $openid = new SimpleOpenID; + $openid = new SimpleOpenID(); $openid->SetIdentity($_GET['openid_identity']); $openid_validation_result = $openid->ValidateWithServer(); if ($openid_validation_result == true){ // OK HERE KEY IS VALID @@ -50,5 +50,3 @@ if(isset($_GET['openid_mode']) and $_GET['openid_mode'] == 'id_res'){ OCP\Util::writeLog('user_openid','USER CANCELED REQUEST',OCP\Util::DEBUG); return false; } - -?> diff --git a/apps/user_openid/appinfo/info.xml b/apps/user_openid/appinfo/info.xml index 268af23973..7aae4271fa 100644 --- a/apps/user_openid/appinfo/info.xml +++ b/apps/user_openid/appinfo/info.xml @@ -8,6 +8,7 @@ 4 true + diff --git a/apps/user_openid/appinfo/version b/apps/user_openid/appinfo/version index 6da28dde76..d917d3e26a 100644 --- a/apps/user_openid/appinfo/version +++ b/apps/user_openid/appinfo/version @@ -1 +1 @@ -0.1.1 \ No newline at end of file +0.1.2 diff --git a/apps/user_openid/l10n/.gitkeep b/apps/user_openid/l10n/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apps/user_openid/l10n/ca.php b/apps/user_openid/l10n/ca.php new file mode 100644 index 0000000000..d203bfb4d9 --- /dev/null +++ b/apps/user_openid/l10n/ca.php @@ -0,0 +1,11 @@ + "Això és un punt final de servidor OpenID. Per més informació consulteu", +"Identity: " => "Identitat:", +"Realm: " => "Domini:", +"User: " => "Usuari:", +"Login" => "Inici de sessió", +"Error: No user Selected" => "Error:No heu seleccionat cap usuari", +"you can authenticate to other sites with this address" => "podeu autenticar altres llocs web amb aquesta adreça", +"Authorized OpenID provider" => "Servidor OpenID autoritzat", +"Your address at Wordpress, Identi.ca, …" => "La vostra adreça a Wordpress, Identi.ca …" +); diff --git a/apps/user_openid/l10n/cs_CZ.php b/apps/user_openid/l10n/cs_CZ.php new file mode 100644 index 0000000000..9c041a89a6 --- /dev/null +++ b/apps/user_openid/l10n/cs_CZ.php @@ -0,0 +1,11 @@ + "Toto je OpenID server endpoint. Více informací na", +"Identity: " => "Identita: ", +"Realm: " => "Oblast: ", +"User: " => "Uživatel: ", +"Login" => "Login", +"Error: No user Selected" => "Chyba: Uživatel není zvolen", +"you can authenticate to other sites with this address" => "s touto adresou se můžete autrorizovat na další strany", +"Authorized OpenID provider" => "Autorizovaný OpenID poskytovatel", +"Your address at Wordpress, Identi.ca, …" => "Vaše adresa na Wordpressu, Identi.ca, …" +); diff --git a/apps/user_openid/l10n/de.php b/apps/user_openid/l10n/de.php new file mode 100644 index 0000000000..7bfe678574 --- /dev/null +++ b/apps/user_openid/l10n/de.php @@ -0,0 +1,11 @@ + "Dies ist ein OpenID-Server-Endpunkt. Weitere Informationen finden Sie unter:", +"Identity: " => "Identität: ", +"Realm: " => "Bereich: ", +"User: " => "Benutzer: ", +"Login" => "Anmelden", +"Error: No user Selected" => "Fehler: Kein Benutzer ausgewählt", +"you can authenticate to other sites with this address" => "Sie können sich auf anderen Seiten mit dieser Adresse authentifizieren.", +"Authorized OpenID provider" => "Authorisierter OpenID-Anbieter", +"Your address at Wordpress, Identi.ca, …" => "Ihre Adresse bei Wordpress, Identi.ca, …" +); diff --git a/apps/user_openid/l10n/el.php b/apps/user_openid/l10n/el.php new file mode 100644 index 0000000000..a80f5bdb24 --- /dev/null +++ b/apps/user_openid/l10n/el.php @@ -0,0 +1,8 @@ +" => "Ταυτότητα: ", +"User: " => "Χρήστης: ", +"Login" => "Σύνδεση", +"Error: No user Selected" => "Σφάλμα: Δεν έχει επιλεχθεί κάποιος χρήστης", +"Authorized OpenID provider" => "Εξουσιοδοτημένος παροχέας OpenID", +"Your address at Wordpress, Identi.ca, …" => "Η διευθυνσή σας σε Wordpress, Identi.ca, …" +); diff --git a/apps/user_openid/l10n/eo.php b/apps/user_openid/l10n/eo.php new file mode 100644 index 0000000000..665d199094 --- /dev/null +++ b/apps/user_openid/l10n/eo.php @@ -0,0 +1,11 @@ + "Ĉi tio estas finpunkto de OpenID-servilo. Por pli da informo, vidu", +"Identity: " => "Idento: ", +"Realm: " => "Regno: ", +"User: " => "Uzanto: ", +"Login" => "Ensaluti", +"Error: No user Selected" => "Eraro: neniu uzanto estas elektita", +"you can authenticate to other sites with this address" => "Vi povas ensaluti en aliaj ejoj per tiu ĉi adreso", +"Authorized OpenID provider" => "Rajtigita OpenID-provizanto", +"Your address at Wordpress, Identi.ca, …" => "Via adreso ĉe Wordpress, Identi.ca…" +); diff --git a/apps/user_openid/l10n/es.php b/apps/user_openid/l10n/es.php new file mode 100644 index 0000000000..048336d3db --- /dev/null +++ b/apps/user_openid/l10n/es.php @@ -0,0 +1,5 @@ +" => "Identidad: ", +"User: " => "Usuario: ", +"Login" => "Iniciar sesión" +); diff --git a/apps/user_openid/l10n/et_EE.php b/apps/user_openid/l10n/et_EE.php new file mode 100644 index 0000000000..f04bcafb24 --- /dev/null +++ b/apps/user_openid/l10n/et_EE.php @@ -0,0 +1,5 @@ + "See on OpenID serveri lõpp-punkt. Lisainfot vaata", +"Identity: " => "Identiteet: ", +"Realm: " => "Tsoon: " +); diff --git a/apps/user_openid/l10n/fi_FI.php b/apps/user_openid/l10n/fi_FI.php new file mode 100644 index 0000000000..1cde4ae660 --- /dev/null +++ b/apps/user_openid/l10n/fi_FI.php @@ -0,0 +1,7 @@ +" => "Identiteetti: ", +"Realm: " => "Alue: ", +"User: " => "Käyttäjä: ", +"Login" => "Kirjaudu", +"Error: No user Selected" => "Virhe: Käyttäjää ei valittu" +); diff --git a/apps/user_openid/l10n/fr.php b/apps/user_openid/l10n/fr.php new file mode 100644 index 0000000000..ec3d00853d --- /dev/null +++ b/apps/user_openid/l10n/fr.php @@ -0,0 +1,11 @@ + "Ce serveur est un point d'accès OpenID. Pour plus d'informations, veuillez consulter", +"Identity: " => "Identité : ", +"Realm: " => "Domaine : ", +"User: " => "Utilisateur : ", +"Login" => "Connexion", +"Error: No user Selected" => "Erreur : Aucun nom d'utilisateur n'a été saisi", +"you can authenticate to other sites with this address" => "vous pouvez vous authentifier sur d'autres sites grâce à cette adresse", +"Authorized OpenID provider" => "Fournisseur d'identité OpenID autorisé", +"Your address at Wordpress, Identi.ca, …" => "Votre adresse Wordpress, Identi.ca, …" +); diff --git a/apps/user_openid/l10n/it.php b/apps/user_openid/l10n/it.php new file mode 100644 index 0000000000..0a9dd951b5 --- /dev/null +++ b/apps/user_openid/l10n/it.php @@ -0,0 +1,11 @@ + "Questo è un server OpenID. Per ulteriori informazioni, vedi ", +"Identity: " => "Identità: ", +"Realm: " => "Dominio: ", +"User: " => "Utente: ", +"Login" => "Accesso", +"Error: No user Selected" => "Errore: nessun utente selezionato", +"you can authenticate to other sites with this address" => "puoi autenticarti ad altri siti con questo indirizzo", +"Authorized OpenID provider" => "Fornitore OpenID autorizzato", +"Your address at Wordpress, Identi.ca, …" => "Il tuo indirizzo su Wordpress, Identi.ca, …" +); diff --git a/apps/user_openid/l10n/ja_JP.php b/apps/user_openid/l10n/ja_JP.php new file mode 100644 index 0000000000..12a2692cb6 --- /dev/null +++ b/apps/user_openid/l10n/ja_JP.php @@ -0,0 +1,11 @@ + "これはOpenIDサーバのエンドポイントです。詳細は下記をチェックしてください。", +"Identity: " => "識別子: ", +"Realm: " => "レルム: ", +"User: " => "ユーザ: ", +"Login" => "ログイン", +"Error: No user Selected" => "エラー: ユーザが選択されていません", +"you can authenticate to other sites with this address" => "他のサイトにこのアドレスで認証することができます", +"Authorized OpenID provider" => "認証されたOpenIDプロバイダ", +"Your address at Wordpress, Identi.ca, …" => "Wordpressのアドレス、Identi.ca、…" +); diff --git a/apps/user_openid/l10n/pl.php b/apps/user_openid/l10n/pl.php new file mode 100644 index 0000000000..c9572851e3 --- /dev/null +++ b/apps/user_openid/l10n/pl.php @@ -0,0 +1,11 @@ + "To jest punkt końcowy serwera OpenID. Aby uzyskać więcej informacji zobacz", +"Identity: " => "Tożsamość: ", +"Realm: " => "Obszar: ", +"User: " => "Użytkownik: ", +"Login" => "Login", +"Error: No user Selected" => "Błąd:Nie wybrano użytkownika", +"you can authenticate to other sites with this address" => "można uwierzytelniać do innych witryn z tego adresu", +"Authorized OpenID provider" => "Autoryzowani dostawcy OpenID", +"Your address at Wordpress, Identi.ca, …" => "Twój adres na Wordpress, Identi.ca, …" +); diff --git a/apps/user_openid/l10n/sl.php b/apps/user_openid/l10n/sl.php new file mode 100644 index 0000000000..0464214a42 --- /dev/null +++ b/apps/user_openid/l10n/sl.php @@ -0,0 +1,11 @@ + "To je OpenID strežniška končna točka. Za več informacij si oglejte", +"Identity: " => "Identiteta: ", +"Realm: " => "Področje: ", +"User: " => "Uporabnik:", +"Login" => "Prijava", +"Error: No user Selected" => "Napaka: Uporabnik ni bil izbran", +"you can authenticate to other sites with this address" => "s tem naslovom se lahko overite tudi na drugih straneh", +"Authorized OpenID provider" => "Odobren ponudnik OpenID", +"Your address at Wordpress, Identi.ca, …" => "Vaš naslov pri Wordpress, Identi.ca, …" +); diff --git a/apps/user_openid/l10n/sv.php b/apps/user_openid/l10n/sv.php new file mode 100644 index 0000000000..fb2c4e2ff7 --- /dev/null +++ b/apps/user_openid/l10n/sv.php @@ -0,0 +1,11 @@ + "Detta är en OpenID-server slutpunkt. För mer information, se", +"Identity: " => "Identitet: ", +"Realm: " => "Realm: ", +"User: " => "Användare: ", +"Login" => "Logga in", +"Error: No user Selected" => "Fel: Ingen användare vald", +"you can authenticate to other sites with this address" => "du kan autentisera till andra webbplatser med denna adress", +"Authorized OpenID provider" => "Godkänd openID leverantör", +"Your address at Wordpress, Identi.ca, …" => "Din adress på Wordpress, Identi.ca, …" +); diff --git a/apps/user_openid/l10n/th_TH.php b/apps/user_openid/l10n/th_TH.php new file mode 100644 index 0000000000..fbf0bbc60a --- /dev/null +++ b/apps/user_openid/l10n/th_TH.php @@ -0,0 +1,11 @@ + "นี่คือปลายทางของเซิร์ฟเวอร์ OpenID สำหรับรายละเอียดเพิ่มเติม, กรุณาดูที่", +"Identity: " => "ข้อมูลประจำตัว ", +"Realm: " => "ขอบเขตพื้นที่ ", +"User: " => "ผู้ใช้งาน: ", +"Login" => "เข้าสู่ระบบ", +"Error: No user Selected" => "พบข้อผิดพลาด ยังไม่ได้เลือกชื่อผู้ใช้งาน", +"you can authenticate to other sites with this address" => "คุณสามารถได้รับสิทธิ์เพื่อเข้าใช้งานเว็บไซต์อื่นๆโดยใช้ที่อยู่นี้", +"Authorized OpenID provider" => "ผู้ให้บริการ OpenID ที่ได้รับอนุญาต", +"Your address at Wordpress, Identi.ca, …" => "ที่อยู่ของคุณที่ Wordpress, Identi.ca, …" +); diff --git a/apps/user_openid/user.php b/apps/user_openid/user.php index 392424795f..88571ba618 100644 --- a/apps/user_openid/user.php +++ b/apps/user_openid/user.php @@ -44,7 +44,4 @@ if(!OCP\User::userExists($USERNAME)){ } $IDENTITY=OCP\Util::linkToAbsolute( "user_openid", "user.php" ).'/'.$USERNAME; -require_once 'phpmyid.php'; - - -?> +require_once 'openid/phpmyid.php'; diff --git a/apps/user_openid/user_openid.php b/apps/user_openid/user_openid.php index 8fb694f75c..9654001cbc 100644 --- a/apps/user_openid/user_openid.php +++ b/apps/user_openid/user_openid.php @@ -21,10 +21,10 @@ * */ -require_once('class.openid.v3.php'); +require_once('openid/class.openid.v3.php'); /** - * Class for user management in a SQL Database (e.g. MySQL, SQLite) + * Class for user OpenId backend */ class OC_USER_OPENID extends OC_User_Backend { /** @@ -37,7 +37,7 @@ class OC_USER_OPENID extends OC_User_Backend { */ public function checkPassword( $uid, $password ){ // Get identity from user and redirect browser to OpenID Server - $openid = new SimpleOpenID; + $openid = new SimpleOpenID(); $openid->SetIdentity($uid); $openid->SetTrustRoot('http://' . OCP\Util::getServerHost()); if ($openid->GetOpenIDServer()){ @@ -63,7 +63,3 @@ class OC_USER_OPENID extends OC_User_Backend { } } } - - - -?> diff --git a/apps/user_webfinger/.htaccess b/apps/user_webfinger/.htaccess new file mode 100644 index 0000000000..1b13cf788f --- /dev/null +++ b/apps/user_webfinger/.htaccess @@ -0,0 +1,5 @@ + +RewriteEngine on +RewriteRule .* - [env=HTTP_AUTHORIZATION:%{HTTP:Authorization}] +RewriteRule ^host-meta host-meta.php [QSA,L] + diff --git a/apps/user_webfinger/appinfo/app.php b/apps/user_webfinger/appinfo/app.php index a45efd96a4..b3d9bbc7f3 100644 --- a/apps/user_webfinger/appinfo/app.php +++ b/apps/user_webfinger/appinfo/app.php @@ -1,7 +1 @@ 11, - 'id' => 'user_webfinger', - 'name' => 'Webfinger' )); -OCP\CONFIG::setAppValue('core', 'public_host-meta', '/apps/user_webfinger/host-meta.php'); -OCP\CONFIG::setAppValue('core', 'public_webfinger', '/apps/user_webfinger/webfinger.php'); diff --git a/apps/user_webfinger/appinfo/info.xml b/apps/user_webfinger/appinfo/info.xml index 4e28814a26..f74e5d3f93 100644 --- a/apps/user_webfinger/appinfo/info.xml +++ b/apps/user_webfinger/appinfo/info.xml @@ -7,4 +7,8 @@ Michiel de Jong, Florian Hülsmann 4 true + + host-meta.php + webfinger.php + diff --git a/apps/user_webfinger/appinfo/version b/apps/user_webfinger/appinfo/version index 1d71ef9744..a2268e2de4 100644 --- a/apps/user_webfinger/appinfo/version +++ b/apps/user_webfinger/appinfo/version @@ -1 +1 @@ -0.3 \ No newline at end of file +0.3.1 \ No newline at end of file diff --git a/apps/user_webfinger/host-meta.php b/apps/user_webfinger/host-meta.php index a4f494ce83..6f6ac46eb9 100644 --- a/apps/user_webfinger/host-meta.php +++ b/apps/user_webfinger/host-meta.php @@ -3,12 +3,28 @@ if (!OCP\App::isEnabled("user_webfinger")) { return; } +if(class_exists('OC')){ + $WEBROOT=OC::$WEBROOT; +}else{//not called trough remote.php try to guess the webroot the best we can from here + // calculate the root directories + $SERVERROOT=str_replace("\\",'/',substr(__FILE__,0,-strlen('apps/user_webfinger/host-meta.php'))); + $WEBROOT=substr($SERVERROOT,strlen(realpath($_SERVER['DOCUMENT_ROOT']))); + + if($WEBROOT!='' and $WEBROOT[0]!=='/'){ + $WEBROOT='/'.$WEBROOT; + } +} + +if(substr($WEBROOT,-1)==='/'){ + $WEBROOT=substr($WEBROOT,0,-1); +} + $hostMetaHeader = array( 'Access-Control-Allow-Origin' => '*', 'Content-Type' => 'application/xrd+json' ); $serverName = $_SERVER['SERVER_NAME']; -$hostMetaContents = '{"links":[{"rel":"lrdd","template":"http'.(isset($_SERVER['HTTPS'])?'s':'').'://'.$serverName.'/public.php?service=webfinger&q={uri}"}]}'; +$hostMetaContents = '{"links":[{"rel":"lrdd","template":"http'.(isset($_SERVER['HTTPS'])?'s':'').'://'.$serverName.$WEBROOT.'/public.php?service=webfinger&q={uri}"}]}'; foreach($hostMetaHeader as $header => $value) { header($header . ": " . $value); } diff --git a/apps/user_webfinger/webfinger.php b/apps/user_webfinger/webfinger.php index 0f882a96cf..b725937948 100644 --- a/apps/user_webfinger/webfinger.php +++ b/apps/user_webfinger/webfinger.php @@ -7,27 +7,20 @@ header("Access-Control-Allow-Origin: *"); header("Content-Type: application/xrd+json"); /** - * To include your app in the webfinger XML, add a new script with file name + * To include your app in the webfinger JSON, add a new script with file name * 'webfinger.php' to /apps/yourapp/appinfo/, which prints out the XML parts * to be included. That script can make use of the constants WF_USER (e. g. * "user"), WF_ID (user@host) and WF_BASEURL (e. g. https://host/owncloud). * An example could look like this: * - * - * + * { + * "rel":"myProfile", + * "type":"text/html", + * "href":"/apps/myApp/profile.php?user=" + * } * * but can also use complex database queries to generate the webfinger result **/ -// calculate the documentroot -// modified version of the one in lib/base.php that takes the .well-known symlink into account -/*$DOCUMENTROOT=realpath($_SERVER['DOCUMENT_ROOT']); -$SERVERROOT=str_replace("\\",'/',dirname(dirname(dirname(dirname(__FILE__))))); -$SUBURI=substr(realpath($_SERVER["SCRIPT_FILENAME"]),strlen($SERVERROOT)); -$WEBROOT=substr($SUBURI,0,-34); -*/ $userName = ''; $hostName = ''; @@ -63,10 +56,9 @@ echo "{\"links\":["; $apps = OC_Appconfig::getApps(); foreach($apps as $app) { if(OCP\App::isEnabled($app)) { - if(is_file(OC::$APPSROOT . '/apps/' . $app . '/appinfo/webfinger.php')) { + if(is_file(OC_App::getAppPath($app). '/appinfo/webfinger.php')) { require($app . '/appinfo/webfinger.php'); } } } echo "]}"; -?> diff --git a/autotest.sh b/autotest.sh new file mode 100755 index 0000000000..a42c6ab059 --- /dev/null +++ b/autotest.sh @@ -0,0 +1,110 @@ +#!/bin/bash +# +# ownCloud +# +# @author Thomas Müller +# @copyright 2012 Thomas Müller thomas.mueller@tmit.eu +# + +DATADIR=data-autotest +BASEDIR=$PWD + +# create autoconfig for sqlite, mysql and (soon) postgresql +cat > ./tests/autoconfig-sqlite.php < false, + 'dbtype' => 'sqlite', + 'dbtableprefix' => 'oc_', + 'adminlogin' => 'admin', + 'adminpass' => 'admin', + 'directory' => '$BASEDIR/$DATADIR', +); +DELIM + +cat > ./tests/autoconfig-mysql.php < false, + 'dbtype' => 'mysql', + 'dbtableprefix' => 'oc_', + 'adminlogin' => 'admin', + 'adminpass' => 'admin', + 'directory' => '$BASEDIR/$DATADIR', + 'dbuser' => 'oc_autotest', + 'dbname' => 'oc_autotest', + 'dbhost' => 'localhost', + 'dbpass' => 'owncloud', +); +DELIM + +cat > ./tests/autoconfig-pgsql.php < false, + 'dbtype' => 'pgsql', + 'dbtableprefix' => 'oc_', + 'adminlogin' => 'admin', + 'adminpass' => 'admin', + 'directory' => '$BASEDIR/$DATADIR', + 'dbuser' => 'oc_autotest', + 'dbname' => 'oc_autotest', + 'dbhost' => 'localhost', + 'dbpass' => 'owncloud', +); +DELIM + +function execute_tests { + echo "Setup environment for $1 testing ..." + # back to root folder + cd $BASEDIR + + # revert changes to tests/data + git checkout tests/data/* + + # reset data directory + rm -rf $DATADIR + mkdir $DATADIR + + # remove the old config file + rm -rf config/config.php + + # drop database + if [ "$1" == "mysql" ] ; then + mysql -u oc_autotest -powncloud -e "DROP DATABASE oc_autotest" + fi + if [ "$1" == "pgsql" ] ; then + dropdb -U oc_autotest oc_autotest + fi + + # copy autoconfig + cp $BASEDIR/tests/autoconfig-$1.php $BASEDIR/config/autoconfig.php + + # trigger installation + php -f index.php + + #test execution + echo "Testing with $1 ..." + cd tests + php -f index.php -- xml $1 > autotest-results-$1.xml +} + +# +# start test execution +# +execute_tests "sqlite" +execute_tests 'mysql' +execute_tests 'pgsql' + +# +# NOTES on mysql: +# - CREATE USER 'oc_autotest'@'localhost' IDENTIFIED BY 'owncloud'; +# - grant access permissions: grant all on oc_autotest.* to 'oc_autotest'@'localhost'; +# +# NOTES on pgsql: +# - su - postgres +# - createuser -P (enter username and password and enable superuser) +# - to enable dropdb I decided to add following line to pg_hba.conf (this is not the safest way but I don't care for the testing machine): +# local all all trust +# + diff --git a/config/config.sample.php b/config/config.sample.php index 7a48918d8b..c4cb719796 100644 --- a/config/config.sample.php +++ b/config/config.sample.php @@ -5,66 +5,104 @@ define("DEBUG", true); $CONFIG = array( /* Flag to indicate OwnCloud is successfully installed (true = installed) */ "installed" => false, + /* Type of database, can be sqlite, mysql or pgsql */ "dbtype" => "sqlite", + /* Name of the OwnCloud database */ "dbname" => "owncloud", + /* User to access the OwnCloud database */ "dbuser" => "", + /* Password to access the OwnCloud database */ "dbpassword" => "", + /* Host running the OwnCloud database */ "dbhost" => "", + /* Prefix for the OwnCloud tables in the database */ "dbtableprefix" => "", + +/* Define the salt used to hash the user passwords. All your user passwords are lost if you lose this string. */ +"passwordsalt" => "", + /* Force use of HTTPS connection (true = use HTTPS) */ "forcessl" => false, + /* Theme to use for OwnCloud */ "theme" => "", + /* Path to the 3rdparty directory */ "3rdpartyroot" => "", + /* URL to the 3rdparty directory, as seen by the browser */ "3rdpartyurl" => "", + /* Default app to load on login */ "defaultapp" => "files", + /* Enable the help menu item in the settings */ "knowledgebaseenabled" => true, + /* URL to use for the help page, server should understand OCS */ "knowledgebaseurl" => "http://api.apps.owncloud.com/v1", + /* Enable installing apps from the appstore */ "appstoreenabled" => true, + /* URL of the appstore to use, server should understand OCS */ "appstoreurl" => "http://api.apps.owncloud.com/v1", + /* Mode to use for sending mail, can be sendmail, smtp, qmail or php, see PHPMailer docs */ "mail_smtpmode" => "sendmail", + /* Host to use for sending mail, depends on mail_smtpmode if this is used */ "mail_smtphost" => "127.0.0.1", + /* authentication needed to send mail, depends on mail_smtpmode if this is used * (false = disable authentication) */ "mail_smtpauth" => false, + /* Username to use for sendmail mail, depends on mail_smtpauth if this is used */ "mail_smtpname" => "", + /* Password to use for sendmail mail, depends on mail_smtpauth if this is used */ "mail_smtppassword" => "", + /* Check 3rdparty apps for malicious code fragments */ "appcodechecker" => "", + +/* Check if ownCloud is up to date */ +"updatechecker" => true, + /* Place to log to, can be owncloud and syslog (owncloud is log menu item in admin menu) */ "log_type" => "owncloud", + /* File for the owncloud logger to log to, (default is ownloud.log in the data dir */ "logfile" => "", + /* Loglevel to start logging at. 0=DEBUG, 1=INFO, 2=WARN, 3=ERROR (default is WARN) */ "loglevel" => "", -"passwordsalt" => "", -"updatechecker" => true, -/* Set this to false to disable the check for writable apps dir. - * If the apps dir is not writable, you can't download&install extra apps - * in the admin apps menu. - */ -"writable_appsdir" => true, + /* The directory where the user data is stored, default to data in the owncloud * directory. The sqlite database is also stored here, when sqlite is used. */ -// "datadirectory" => "" +// "datadirectory" => "", + +"apps_paths" => array( + +/* Set an array of path for your apps directories + key 'path' is for the fs path an the key 'url' is for the http path to your + applications paths. 'writable' indicate if the user can install apps in this folder. + You must have at least 1 app folder writable or you must set the parameter : appstoreenabled to false +*/ + array( + 'path'=> '/var/www/owncloud/apps', + 'url' => '/apps', + 'writable' => true, + ), + ), ); -?> + diff --git a/core/ajax/share.php b/core/ajax/share.php new file mode 100644 index 0000000000..04294a36ac --- /dev/null +++ b/core/ajax/share.php @@ -0,0 +1,126 @@ +. +*/ +require_once '../../lib/base.php'; + +OC_JSON::checkLoggedIn(); +if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSource'])) { + switch ($_POST['action']) { + case 'share': + if (isset($_POST['shareType']) && isset($_POST['shareWith']) && isset($_POST['permissions'])) { + try { + OCP\Share::shareItem($_POST['itemType'], $_POST['itemSource'], (int)$_POST['shareType'], $_POST['shareWith'], $_POST['permissions']); + // TODO May need to return private link + OC_JSON::success(); + } catch (Exception $exception) { + OC_JSON::error(array('data' => array('message' => $exception->getMessage()))); + } + } + break; + case 'unshare': + if (isset($_POST['shareType']) && isset($_POST['shareWith'])) { + $return = OCP\Share::unshare($_POST['itemType'], $_POST['itemSource'], $_POST['shareType'], $_POST['shareWith']); + ($return) ? OC_JSON::success() : OC_JSON::error(); + } + break; + case 'setPermissions': + if (isset($_POST['shareType']) && isset($_POST['shareWith']) && isset($_POST['permissions'])) { + $return = OCP\Share::setPermissions($_POST['itemType'], $_POST['itemSource'], $_POST['shareType'], $_POST['shareWith'], $_POST['permissions']); + ($return) ? OC_JSON::success() : OC_JSON::error(); + } + break; + } +} else if (isset($_GET['fetch'])) { + switch ($_GET['fetch']) { + case 'getItemsSharedStatuses': + if (isset($_GET['itemType'])) { + $return = OCP\Share::getItemsShared($_GET['itemType'], OCP\Share::FORMAT_STATUSES); + is_array($return) ? OC_JSON::success(array('data' => $return)) : OC_JSON::error(); + } + break; + case 'getItem': + if (isset($_GET['itemType']) && isset($_GET['itemSource']) && isset($_GET['checkShares'])) { + $reshare = OCP\Share::getItemSharedWithBySource($_GET['itemType'], $_GET['itemSource'], OCP\Share::FORMAT_NONE, null, true); + if ($_GET['checkShares'] == "true") { + $shares = OCP\Share::getItemShared($_GET['itemType'], $_GET['itemSource']); + } else { + $shares = false; + } + OC_JSON::success(array('data' => array('reshare' => $reshare, 'shares' => $shares))); + } + break; + case 'getShareWith': + if (isset($_GET['search'])) { + $shareWith = array(); + if (OC_App::isEnabled('contacts')) { + // TODO Add function to contacts to only get the 'fullname' column to improve performance + $ids = OC_Contacts_Addressbook::activeIds(); + foreach ($ids as $id) { + $vcards = OC_Contacts_VCard::all($id); + foreach ($vcards as $vcard) { + $contact = $vcard['fullname']; + if (stripos($contact, $_GET['search']) !== false + && (!isset($_GET['itemShares']) + || !isset($_GET['itemShares'][OCP\Share::SHARE_TYPE_CONTACT]) + || !is_array($_GET['itemShares'][OCP\Share::SHARE_TYPE_CONTACT]) + || !in_array($contact, $_GET['itemShares'][OCP\Share::SHARE_TYPE_CONTACT]))) { + $shareWith[] = array('label' => $contact, 'value' => array('shareType' => 5, 'shareWith' => $vcard['id'])); + } + } + } + } + $count = 0; + $users = array(); + $limit = 0; + $offset = 0; + while ($count < 4 && count($users) == $limit) { + $limit = 4 - $count; + $users = OC_User::getUsers($_GET['search'], $limit, $offset); + $offset += $limit; + foreach ($users as $user) { + if ((!isset($_GET['itemShares']) || !is_array($_GET['itemShares'][OCP\Share::SHARE_TYPE_USER]) || !in_array($user, $_GET['itemShares'][OCP\Share::SHARE_TYPE_USER])) && $user != OC_User::getUser()) { + $shareWith[] = array('label' => $user, 'value' => array('shareType' => OCP\Share::SHARE_TYPE_USER, 'shareWith' => $user)); + $count++; + } + } + } + $count = 0; + $groups = OC_Group::getUserGroups(OC_User::getUser()); + foreach ($groups as $group) { + if ($count < 4) { + if (stripos($group, $_GET['search']) !== false + && (!isset($_GET['itemShares']) + || !isset($_GET['itemShares'][OCP\Share::SHARE_TYPE_GROUP]) + || !is_array($_GET['itemShares'][OCP\Share::SHARE_TYPE_GROUP]) + || !in_array($group, $_GET['itemShares'][OCP\Share::SHARE_TYPE_GROUP]))) { + $shareWith[] = array('label' => $group.' (group)', 'value' => array('shareType' => OCP\Share::SHARE_TYPE_GROUP, 'shareWith' => $group)); + $count++; + } + } else { + break; + } + } + OC_JSON::success(array('data' => $shareWith)); + } + break; + } +} + +?> \ No newline at end of file diff --git a/core/ajax/translations.php b/core/ajax/translations.php index a6433b1964..2bd6b7ed63 100644 --- a/core/ajax/translations.php +++ b/core/ajax/translations.php @@ -29,4 +29,3 @@ $app = $_POST["app"]; $l = OC_L10N::get( $app ); OC_JSON::success(array('data' => $l->getTranslations())); -?> diff --git a/core/ajax/vcategories/add.php b/core/ajax/vcategories/add.php index a58489228d..e69f8bb726 100644 --- a/core/ajax/vcategories/add.php +++ b/core/ajax/vcategories/add.php @@ -39,5 +39,3 @@ if($categories->hasCategory($category)) { } OC_JSON::success(array('data' => array('categories'=>$categories->categories()))); - -?> diff --git a/core/ajax/vcategories/delete.php b/core/ajax/vcategories/delete.php index 75def433d3..a41fa083c3 100644 --- a/core/ajax/vcategories/delete.php +++ b/core/ajax/vcategories/delete.php @@ -34,5 +34,3 @@ if(is_null($categories)) { $vcategories = new OC_VCategories($app); $vcategories->delete($categories); OC_JSON::success(array('data' => array('categories'=>$vcategories->categories()))); - -?> diff --git a/core/ajax/vcategories/edit.php b/core/ajax/vcategories/edit.php index 252b3d3454..3e5540cbc2 100644 --- a/core/ajax/vcategories/edit.php +++ b/core/ajax/vcategories/edit.php @@ -31,5 +31,3 @@ $categories = $vcategories->categories(); debug(print_r($categories, true)); $tmpl->assign('categories',$categories); $tmpl->printpage(); - -?> diff --git a/apps/files_sharing/css/sharing.css b/core/css/share.css similarity index 54% rename from apps/files_sharing/css/sharing.css rename to core/css/share.css index c4b4540e9d..a683a24a11 100644 --- a/apps/files_sharing/css/sharing.css +++ b/core/css/share.css @@ -2,13 +2,16 @@ This file is licensed under the Affero General Public License version 3 or later. See the COPYING-README file. */ -#dropdown { display:block; position:absolute; z-index:100; width:16em; right:0; margin-right:7em; background:#eee; padding:1em; +#dropdown { display:block; position:absolute; z-index:500; width:16em; right:0; margin-right:7em; background:#eee; padding:1em; -moz-box-shadow:0 1px 1px #777; -webkit-box-shadow:0 1px 1px #777; box-shadow:0 1px 1px #777; -moz-border-radius-bottomleft:1em; -webkit-border-bottom-left-radius:1em; border-bottom-left-radius:1em; -moz-border-radius-bottomright:1em; -webkit-border-bottom-right-radius:1em; border-bottom-right-radius:1em; } -#sharedWithList { padding:0.5em; list-style-type: none; } -#privateLink { border-top:1px solid #ddd; padding-top:0.5em; } -a.unshare { float:right; display:inline; margin:0 .5em; padding:.3em .3em 0 .3em !important; opacity:.5; } +#shareWithList { padding:0.5em; list-style-type: none; } +#shareWithList li { padding-top:0.1em; } +#dropdown label { font-weight:normal; } +#dropdown input[type="checkbox"] { margin:0 0.2em 0 0.5em; } +a.showCruds { display:inline; opacity:.5; } +a.showCruds:hover { opacity:1; } +a.unshare { float:right; display:inline; padding:.3em 0 0 .3em !important; opacity:.5; } a.unshare:hover { opacity:1; } -#share_with { width: 16em; } -#privateLink label, .edit { font-weight:normal; } +#privateLink { border-top:1px solid #ddd; padding-top:0.5em; } \ No newline at end of file diff --git a/core/css/styles.css b/core/css/styles.css index 186c11b4ff..dd6f9d4675 100644 --- a/core/css/styles.css +++ b/core/css/styles.css @@ -3,7 +3,7 @@ See the COPYING-README file. */ html, body, div, span, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, code, del, dfn, em, img, q, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, dialog, figure, footer, header, hgroup, nav, section { margin:0; padding:0; border:0; outline:0; font-weight:inherit; font-size:100%; font-family:inherit; vertical-align:baseline; cursor:default; } -html, body { height: 100%; } +html, body { height: 100%; overflow: auto; } article, aside, dialog, figure, footer, header, hgroup, nav, section { display:block; } body { line-height:1.5; } table { border-collapse:separate; border-spacing:0; white-space:nowrap; } @@ -16,7 +16,7 @@ body { background:#fefefe; font:normal .8em/1.6em "Lucida Grande", Arial, Verdan /* HEADERS */ -#body-user #header, #body-settings #header { position:fixed; top:0; z-index:100; width:100%; height:2.5em; padding:.5em; background:#1d2d44; -moz-box-shadow:0 0 10px rgba(0, 0, 0, .5), inset 0 -2px 10px #222; -webkit-box-shadow:0 0 10px rgba(0, 0, 0, .5), inset 0 -2px 10px #222; box-shadow:0 0 10px rgba(0, 0, 0, .5), inset 0 -2px 10px #222; } +#body-user #header, #body-settings #header { position:fixed; top:0; left:0; right:0; z-index:100; height:2.5em; line-height:2.5em; padding:.5em; background:#1d2d44; -moz-box-shadow:0 0 10px rgba(0, 0, 0, .5), inset 0 -2px 10px #222; -webkit-box-shadow:0 0 10px rgba(0, 0, 0, .5), inset 0 -2px 10px #222; box-shadow:0 0 10px rgba(0, 0, 0, .5), inset 0 -2px 10px #222; } #body-login #header { margin: -2em auto 0; text-align:center; height:10em; padding:1em 0 .5em; -moz-box-shadow:0 0 1em rgba(0, 0, 0, .5); -webkit-box-shadow:0 0 1em rgba(0, 0, 0, .5); box-shadow:0 0 1em rgba(0, 0, 0, .5); background: #1d2d44; /* Old browsers */ @@ -28,8 +28,9 @@ background: -ms-linear-gradient(top, #35537a 0%,#1d2d42 100%); /* IE10+ */ background: linear-gradient(top, #35537a 0%,#1d2d42 100%); /* W3C */ filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#35537a', endColorstr='#1d2d42',GradientType=0 ); /* IE6-9 */ } -#owncloud { float:left; } - +#owncloud { float:left; vertical-align:middle; } +.header-right { float:right; vertical-align:middle; padding:0 0.5em; } +.header-right > * { vertical-align:middle; } /* INPUTS */ input[type="text"], input[type="password"] { cursor:text; } @@ -49,7 +50,7 @@ input[type="checkbox"] { width:auto; } #body-login input[type="text"], #body-login input[type="password"] { width: 13em; } #body-login input.login { width: auto; float: right; } #remember_login { margin:.8em .2em 0 1em; } -.searchbox input[type="search"] { position:fixed; font-size:1.2em; top:.4em; right:3em; padding:.2em .5em .2em 1.5em; background:#fff url('../img/actions/search.svg') no-repeat .5em center; border:0; -moz-border-radius:1em; -webkit-border-radius:1em; border-radius:1em; -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=70)"; filter:alpha(opacity=70);opacity:.7; -webkit-transition:opacity 300ms; -moz-transition:opacity 300ms; -o-transition:opacity 300ms; transition:opacity 300ms; } +.searchbox input[type="search"] { font-size:1.2em; padding:.2em .5em .2em 1.5em; background:#fff url('../img/actions/search.svg') no-repeat .5em center; border:0; -moz-border-radius:1em; -webkit-border-radius:1em; border-radius:1em; -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=70)"; filter:alpha(opacity=70);opacity:.7; -webkit-transition:opacity 300ms; -moz-transition:opacity 300ms; -o-transition:opacity 300ms; transition:opacity 300ms; } input[type="submit"].enabled { background:#66f866; border:1px solid #5e5; -moz-box-shadow:0 1px 1px #f8f8f8, 0 1px 1px #cfc inset; -webkit-box-shadow:0 1px 1px #f8f8f8, 0 1px 1px #cfc inset; box-shadow:0 1px 1px #f8f8f8, 0 1px 1px #cfc inset; } input[type="submit"].highlight{ background:#ffc100; border:1px solid #db0; text-shadow:#ffeedd 0 1px 0; -moz-box-shadow:0 1px 1px #f8f8f8, 0 1px 1px #ffeedd inset; -webkit-box-shadow:0 1px 1px #f8f8f8, 0 1px 1px #ffeedd inset; box-shadow:0 1px 1px #f8f8f8, 0 1px 1px #ffeedd inset; } #select_all{ margin-top: .4em !important;} @@ -101,19 +102,19 @@ label.infield { cursor: text !important; } #expand { position:relative; z-index:100; margin-bottom:-.5em; padding:.5em 10.1em .7em 1.2em; cursor:pointer; } #expand+span { position:absolute; z-index:99; margin:-1.7em 0 0 2.5em; font-size:1.2em; color:#666; text-shadow:#f8f8f8 0 1px 0; -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; filter:alpha(opacity=0); opacity:0; -webkit-transition:opacity 300ms; -moz-transition:opacity 300ms; -o-transition:opacity 300ms; transition:opacity 300ms; } #expand:hover+span, #expand+span:hover { -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; filter:alpha(opacity=100); opacity:1; cursor:pointer; } -#logout { position:absolute; right:0; top:0; padding:1.2em 2em .55em 1.2em; } - /* VARIOUS REUSABLE SELECTORS */ .hidden { display:none; } .bold { font-weight: bold; } +.center { text-align: center; } -#notification { z-index:101; cursor:pointer; background-color:#fc4; border:0; padding:0 .7em .3em; display:none; position:fixed; left:50%; top:0; -moz-border-radius-bottomleft:1em; -webkit-border-bottom-left-radius:1em; border-bottom-left-radius:1em; -moz-border-radius-bottomright:1em; -webkit-border-bottom-right-radius:1em; border-bottom-right-radius:1em; } +#notification { z-index:101; background-color:#fc4; border:0; padding:0 .7em .3em; display:none; position:fixed; left:50%; top:0; -moz-border-radius-bottomleft:1em; -webkit-border-bottom-left-radius:1em; border-bottom-left-radius:1em; -moz-border-radius-bottomright:1em; -webkit-border-bottom-right-radius:1em; border-bottom-right-radius:1em; } +#notification span { cursor:pointer; font-weight:bold; margin-left:1em; } .action, .selectedActions a { -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; filter:alpha(opacity=50); opacity:.5; -webkit-transition:opacity 200ms; -moz-transition:opacity 200ms; -o-transition:opacity 200ms; transition:opacity 200ms; } .action { width: 16px; height: 16px; } -#logout { -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=80)"; filter:alpha(opacity=80); opacity:.8; } -.action:hover, .selectedActions a:hover, #logout:hover { -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; filter:alpha(opacity=100); opacity:1; } +.header-action { -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=80)"; filter:alpha(opacity=80); opacity:.8; } +.action:hover, .selectedActions a:hover, .header-action:hover { -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; filter:alpha(opacity=100); opacity:1; } table:not(.nostyle) tr { -webkit-transition:background-color 200ms; -moz-transition:background-color 200ms; -o-transition:background-color 200ms; transition:background-color 200ms; } tbody tr:hover, tr:active { background-color:#f8f8f8; } @@ -134,11 +135,13 @@ div.jp-play-bar, div.jp-seek-bar { padding:0; } li.error { width:640px; margin:4em auto; padding:1em 1em 1em 4em; background:#ffe .8em .8em no-repeat; color: #FF3B3B; border:1px solid #ccc; -moz-border-radius:10px; -webkit-border-radius:10px; border-radius:10px; } .ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { overflow: hidden; text-overflow: ellipsis; } -.hint { background-image: url('/core/img/actions/info.png'); background-repeat:no-repeat; color: #777777; padding-left: 25px; background-position: 0 0.3em;} +.hint { background-image: url('../img/actions/info.png'); background-repeat:no-repeat; color: #777777; padding-left: 25px; background-position: 0 0.3em;} .separator { display: inline; border-left: 1px solid #d3d3d3; border-right: 1px solid #fff; height: 10px; width:0px; margin: 4px; } a.bookmarklet { background-color: #ddd; border:1px solid #ccc; padding: 5px;padding-top: 0px;padding-bottom: 2px; text-decoration: none; margin-top: 5px } +.exception{color: #000000;} +.exception textarea{width:95%;height: 200px;background:#ffe;border:0;} /* ---- DIALOGS ---- */ #dirtree {width: 100%;} @@ -155,3 +158,14 @@ a.bookmarklet { background-color: #ddd; border:1px solid #ccc; padding: 5px;padd #categorylist li { background:#f8f8f8; padding:.3em .8em; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; -webkit-transition:background-color 500ms; -moz-transition:background-color 500ms; -o-transition:background-color 500ms; transition:background-color 500ms; } #categorylist li:hover, li:active { background:#eee; } #category_addinput { width: 10em; } + +/* ---- APP SETTINGS ---- */ +.popup { background-color: white; border-radius: 10px 10px 10px 10px; box-shadow: 0 0 20px #888888; color: #333333; padding: 10px; position: fixed !important; z-index: 200; } +.popup.topright { top: 7em; right: 1em; } +.popup.bottomleft { bottom: 1em; left: 33em; } +.popup .close { position:absolute; top: 0.2em; right:0.2em; height: 20px; width: 20px; background:url('../img/actions/delete.svg') no-repeat center; } +.popup h2 { font-weight: bold; font-size: 1.2em; } +.arrow { border-bottom: 10px solid white; border-left: 10px solid transparent; border-right: 10px solid transparent; display: block; height: 0; position: absolute; width: 0; z-index: 201; } +.arrow.left { left: -13px; bottom: 1.2em; -webkit-transform: rotate(270deg); -moz-transform: rotate(270deg); -o-transform: rotate(270deg); -ms-transform: rotate(270deg); transform: rotate(270deg); } +.arrow.up { top: -8px; right: 2em; } +.arrow.down { -webkit-transform: rotate(180deg); -moz-transform: rotate(180deg); -o-transform: rotate(180deg); -ms-transform: rotate(180deg); transform: rotate(180deg); } diff --git a/core/img/actions/add.png b/core/img/actions/add.png index db1b1970f1..25d472b2dc 100644 Binary files a/core/img/actions/add.png and b/core/img/actions/add.png differ diff --git a/core/img/actions/delete.png b/core/img/actions/delete.png index bc0c782882..fa8e18183e 100644 Binary files a/core/img/actions/delete.png and b/core/img/actions/delete.png differ diff --git a/core/img/actions/download.png b/core/img/actions/download.png index 14e88e14c0..65954f941b 100644 Binary files a/core/img/actions/download.png and b/core/img/actions/download.png differ diff --git a/core/img/actions/history.png b/core/img/actions/history.png index b1e743651f..1d138b8cd5 100644 Binary files a/core/img/actions/history.png and b/core/img/actions/history.png differ diff --git a/core/img/actions/info.png b/core/img/actions/info.png index 2257d144d1..37ccb35683 100644 Binary files a/core/img/actions/info.png and b/core/img/actions/info.png differ diff --git a/core/img/actions/logout.png b/core/img/actions/logout.png index 74dcd33bee..eb2ea766c3 100644 Binary files a/core/img/actions/logout.png and b/core/img/actions/logout.png differ diff --git a/core/img/actions/mail.png b/core/img/actions/mail.png index 4d3192ef32..8e884fbc0e 100644 Binary files a/core/img/actions/mail.png and b/core/img/actions/mail.png differ diff --git a/core/img/actions/pause-big.png b/core/img/actions/pause-big.png index 9bcfd0406d..1c4cf503b8 100644 Binary files a/core/img/actions/pause-big.png and b/core/img/actions/pause-big.png differ diff --git a/core/img/actions/pause.png b/core/img/actions/pause.png index ced8c43ab3..f74ed3a861 100644 Binary files a/core/img/actions/pause.png and b/core/img/actions/pause.png differ diff --git a/core/img/actions/play-add.png b/core/img/actions/play-add.png index 0c330d4ac4..0097f671ae 100644 Binary files a/core/img/actions/play-add.png and b/core/img/actions/play-add.png differ diff --git a/core/img/actions/play-big.png b/core/img/actions/play-big.png index 3ccd36129e..2da2426dcf 100644 Binary files a/core/img/actions/play-big.png and b/core/img/actions/play-big.png differ diff --git a/core/img/actions/play-next.png b/core/img/actions/play-next.png index 0c0ccc87cd..08568b3dc0 100644 Binary files a/core/img/actions/play-next.png and b/core/img/actions/play-next.png differ diff --git a/core/img/actions/play-previous.png b/core/img/actions/play-previous.png index d98cedaa1e..811cde46c1 100644 Binary files a/core/img/actions/play-previous.png and b/core/img/actions/play-previous.png differ diff --git a/core/img/actions/play.png b/core/img/actions/play.png index a252a75155..adbef1e576 100644 Binary files a/core/img/actions/play.png and b/core/img/actions/play.png differ diff --git a/core/img/actions/public.png b/core/img/actions/public.png index 75d1366326..9e56f2919f 100644 Binary files a/core/img/actions/public.png and b/core/img/actions/public.png differ diff --git a/core/img/actions/rename.png b/core/img/actions/rename.png index 9993a092df..3af6840071 100644 Binary files a/core/img/actions/rename.png and b/core/img/actions/rename.png differ diff --git a/core/img/actions/search.png b/core/img/actions/search.png index bfedb80bb5..98e1d73ee3 100644 Binary files a/core/img/actions/search.png and b/core/img/actions/search.png differ diff --git a/core/img/actions/settings.png b/core/img/actions/settings.png index 5b1607e59f..8b3acb00a4 100644 Binary files a/core/img/actions/settings.png and b/core/img/actions/settings.png differ diff --git a/core/img/actions/share.png b/core/img/actions/share.png index 62c4627f31..099e4d6ab3 100644 Binary files a/core/img/actions/share.png and b/core/img/actions/share.png differ diff --git a/core/img/actions/shared.png b/core/img/actions/shared.png index 073ff74168..6e112e75b4 100644 Binary files a/core/img/actions/shared.png and b/core/img/actions/shared.png differ diff --git a/core/img/actions/sound-off.png b/core/img/actions/sound-off.png index 7900e500c9..2eddb00af0 100644 Binary files a/core/img/actions/sound-off.png and b/core/img/actions/sound-off.png differ diff --git a/core/img/actions/sound.png b/core/img/actions/sound.png index 838c9cee17..9349c94e7a 100644 Binary files a/core/img/actions/sound.png and b/core/img/actions/sound.png differ diff --git a/core/img/actions/triangle-s.png b/core/img/actions/triangle-s.png index d77d5db2ca..53590a2197 100644 Binary files a/core/img/actions/triangle-s.png and b/core/img/actions/triangle-s.png differ diff --git a/core/img/actions/upload-white.png b/core/img/actions/upload-white.png index 09dba9e910..fd9bdccc24 100644 Binary files a/core/img/actions/upload-white.png and b/core/img/actions/upload-white.png differ diff --git a/core/img/actions/upload.png b/core/img/actions/upload.png index 5744aad75a..1d90165a55 100644 Binary files a/core/img/actions/upload.png and b/core/img/actions/upload.png differ diff --git a/core/img/breadcrumb-start.png b/core/img/breadcrumb-start.png index a79d675454..b0df5f4403 100644 Binary files a/core/img/breadcrumb-start.png and b/core/img/breadcrumb-start.png differ diff --git a/core/img/breadcrumb.png b/core/img/breadcrumb.png index b124f349f5..84992be0d9 100644 Binary files a/core/img/breadcrumb.png and b/core/img/breadcrumb.png differ diff --git a/core/img/favicon-touch.png b/core/img/favicon-touch.png index cfaaa4399a..24770fb634 100644 Binary files a/core/img/favicon-touch.png and b/core/img/favicon-touch.png differ diff --git a/core/img/favicon.png b/core/img/favicon.png index c1b1cb6546..79b6795f6f 100644 Binary files a/core/img/favicon.png and b/core/img/favicon.png differ diff --git a/core/img/filetypes/application-msexcel.png b/core/img/filetypes/application-msexcel.png new file mode 100644 index 0000000000..abcd93689a Binary files /dev/null and b/core/img/filetypes/application-msexcel.png differ diff --git a/core/img/filetypes/application-mspowerpoint.png b/core/img/filetypes/application-mspowerpoint.png new file mode 100644 index 0000000000..b4aaad9a45 Binary files /dev/null and b/core/img/filetypes/application-mspowerpoint.png differ diff --git a/core/img/filetypes/application-msword.png b/core/img/filetypes/application-msword.png new file mode 100644 index 0000000000..e8b230c59c Binary files /dev/null and b/core/img/filetypes/application-msword.png differ diff --git a/core/img/filetypes/application-sgf.png b/core/img/filetypes/application-sgf.png index f171f5579e..48996c5439 100644 Binary files a/core/img/filetypes/application-sgf.png and b/core/img/filetypes/application-sgf.png differ diff --git a/core/img/filetypes/application-vnd.oasis.opendocument.formula.png b/core/img/filetypes/application-vnd.oasis.opendocument.formula.png index 4cefbb690d..e0cf49542d 100644 Binary files a/core/img/filetypes/application-vnd.oasis.opendocument.formula.png and b/core/img/filetypes/application-vnd.oasis.opendocument.formula.png differ diff --git a/core/img/filetypes/application-vnd.oasis.opendocument.graphics.png b/core/img/filetypes/application-vnd.oasis.opendocument.graphics.png index 3d66cc97eb..b326a0543a 100644 Binary files a/core/img/filetypes/application-vnd.oasis.opendocument.graphics.png and b/core/img/filetypes/application-vnd.oasis.opendocument.graphics.png differ diff --git a/core/img/filetypes/application-vnd.oasis.opendocument.presentation.png b/core/img/filetypes/application-vnd.oasis.opendocument.presentation.png index 46942cba28..7c6fd24684 100644 Binary files a/core/img/filetypes/application-vnd.oasis.opendocument.presentation.png and b/core/img/filetypes/application-vnd.oasis.opendocument.presentation.png differ diff --git a/core/img/filetypes/application-vnd.oasis.opendocument.spreadsheet.png b/core/img/filetypes/application-vnd.oasis.opendocument.spreadsheet.png index abc38d4310..8b0e85b067 100644 Binary files a/core/img/filetypes/application-vnd.oasis.opendocument.spreadsheet.png and b/core/img/filetypes/application-vnd.oasis.opendocument.spreadsheet.png differ diff --git a/core/img/filetypes/application-vnd.oasis.opendocument.text.png b/core/img/filetypes/application-vnd.oasis.opendocument.text.png index 06c1f30c8f..48452eb3e8 100644 Binary files a/core/img/filetypes/application-vnd.oasis.opendocument.text.png and b/core/img/filetypes/application-vnd.oasis.opendocument.text.png differ diff --git a/core/img/filetypes/application-x-7z-compressed.png b/core/img/filetypes/application-x-7z-compressed.png index 55dd0f7536..2cd08aebf9 100644 Binary files a/core/img/filetypes/application-x-7z-compressed.png and b/core/img/filetypes/application-x-7z-compressed.png differ diff --git a/core/img/filetypes/application-x-bzip-compressed-tar.png b/core/img/filetypes/application-x-bzip-compressed-tar.png index 55dd0f7536..2cd08aebf9 100644 Binary files a/core/img/filetypes/application-x-bzip-compressed-tar.png and b/core/img/filetypes/application-x-bzip-compressed-tar.png differ diff --git a/core/img/filetypes/application-x-bzip.png b/core/img/filetypes/application-x-bzip.png index 55dd0f7536..2cd08aebf9 100644 Binary files a/core/img/filetypes/application-x-bzip.png and b/core/img/filetypes/application-x-bzip.png differ diff --git a/core/img/filetypes/application-x-compressed-tar.png b/core/img/filetypes/application-x-compressed-tar.png index 55dd0f7536..2cd08aebf9 100644 Binary files a/core/img/filetypes/application-x-compressed-tar.png and b/core/img/filetypes/application-x-compressed-tar.png differ diff --git a/core/img/filetypes/application-x-deb.png b/core/img/filetypes/application-x-deb.png index 55dd0f7536..2cd08aebf9 100644 Binary files a/core/img/filetypes/application-x-deb.png and b/core/img/filetypes/application-x-deb.png differ diff --git a/core/img/filetypes/application-x-debian-package.png b/core/img/filetypes/application-x-debian-package.png index eff1b7fc8c..b3f6b7e5cf 100644 Binary files a/core/img/filetypes/application-x-debian-package.png and b/core/img/filetypes/application-x-debian-package.png differ diff --git a/core/img/filetypes/application-x-gzip.png b/core/img/filetypes/application-x-gzip.png index 55dd0f7536..2cd08aebf9 100644 Binary files a/core/img/filetypes/application-x-gzip.png and b/core/img/filetypes/application-x-gzip.png differ diff --git a/core/img/filetypes/application-x-lzma-compressed-tar.png b/core/img/filetypes/application-x-lzma-compressed-tar.png index 55dd0f7536..2cd08aebf9 100644 Binary files a/core/img/filetypes/application-x-lzma-compressed-tar.png and b/core/img/filetypes/application-x-lzma-compressed-tar.png differ diff --git a/core/img/filetypes/application-x-rar.png b/core/img/filetypes/application-x-rar.png index 55dd0f7536..2cd08aebf9 100644 Binary files a/core/img/filetypes/application-x-rar.png and b/core/img/filetypes/application-x-rar.png differ diff --git a/core/img/filetypes/application-x-rpm.png b/core/img/filetypes/application-x-rpm.png index 55dd0f7536..2cd08aebf9 100644 Binary files a/core/img/filetypes/application-x-rpm.png and b/core/img/filetypes/application-x-rpm.png differ diff --git a/core/img/filetypes/application-x-tar.png b/core/img/filetypes/application-x-tar.png index 55dd0f7536..2cd08aebf9 100644 Binary files a/core/img/filetypes/application-x-tar.png and b/core/img/filetypes/application-x-tar.png differ diff --git a/core/img/filetypes/application-x-tarz.png b/core/img/filetypes/application-x-tarz.png index 55dd0f7536..2cd08aebf9 100644 Binary files a/core/img/filetypes/application-x-tarz.png and b/core/img/filetypes/application-x-tarz.png differ diff --git a/core/img/filetypes/application-zip.png b/core/img/filetypes/application-zip.png index 55dd0f7536..2cd08aebf9 100644 Binary files a/core/img/filetypes/application-zip.png and b/core/img/filetypes/application-zip.png differ diff --git a/core/img/filetypes/flash.png b/core/img/filetypes/flash.png index 5769120b1b..9f5db634a4 100644 Binary files a/core/img/filetypes/flash.png and b/core/img/filetypes/flash.png differ diff --git a/core/img/icon-error.png b/core/img/icon-error.png index ed438a32fd..1ce0be0fb2 100644 Binary files a/core/img/icon-error.png and b/core/img/icon-error.png differ diff --git a/core/img/icon-sync.png b/core/img/icon-sync.png index 99a43d4c69..a3d0970424 100644 Binary files a/core/img/icon-sync.png and b/core/img/icon-sync.png differ diff --git a/core/img/icon.png b/core/img/icon.png index 24a4b1c3e8..745b82584c 100644 Binary files a/core/img/icon.png and b/core/img/icon.png differ diff --git a/core/img/logo-inverted.png b/core/img/logo-inverted.png index d9fd119dc1..265a8871b4 100644 Binary files a/core/img/logo-inverted.png and b/core/img/logo-inverted.png differ diff --git a/core/img/logo-square.png b/core/img/logo-square.png index 086d415db6..b836de8f3b 100644 Binary files a/core/img/logo-square.png and b/core/img/logo-square.png differ diff --git a/core/img/logo-wide.png b/core/img/logo-wide.png index ea10828db5..702f1d97e5 100644 Binary files a/core/img/logo-wide.png and b/core/img/logo-wide.png differ diff --git a/core/img/logo.png b/core/img/logo.png index 8177c4cdba..a84fe145bb 100644 Binary files a/core/img/logo.png and b/core/img/logo.png differ diff --git a/core/img/places/file.png b/core/img/places/file.png index 4979044889..63837a1af9 100644 Binary files a/core/img/places/file.png and b/core/img/places/file.png differ diff --git a/core/img/places/folder.png b/core/img/places/folder.png index 3edbe257a3..46079e03e9 100644 Binary files a/core/img/places/folder.png and b/core/img/places/folder.png differ diff --git a/core/img/places/home.png b/core/img/places/home.png index b3fb9bbaf6..c3dbd3e353 100644 Binary files a/core/img/places/home.png and b/core/img/places/home.png differ diff --git a/core/img/places/music.png b/core/img/places/music.png index 4c844425d6..85ee2474cd 100644 Binary files a/core/img/places/music.png and b/core/img/places/music.png differ diff --git a/core/img/places/picture.png b/core/img/places/picture.png index 980a7c6981..9abcd09722 100644 Binary files a/core/img/places/picture.png and b/core/img/places/picture.png differ diff --git a/core/img/remoteStorage-big.png b/core/img/remoteStorage-big.png index 7c429a6a73..f225423303 100644 Binary files a/core/img/remoteStorage-big.png and b/core/img/remoteStorage-big.png differ diff --git a/core/js/backgroundjobs.js b/core/js/backgroundjobs.js new file mode 100644 index 0000000000..4a558a66b4 --- /dev/null +++ b/core/js/backgroundjobs.js @@ -0,0 +1,25 @@ +/** +* ownCloud +* +* @author Jakob Sack +* @copyright 2012 Jakob Sack owncloud@jakobsack.de +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE +* License as published by the Free Software Foundation; either +* version 3 of the License, or any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU AFFERO GENERAL PUBLIC LICENSE for more details. +* +* You should have received a copy of the GNU Affero General Public +* License along with this library. If not, see . +* +*/ + +// start worker once page has loaded +$(document).ready(function(){ + $.get( OC.webroot+'/cron.php' ); +}); diff --git a/core/js/eventsource.js b/core/js/eventsource.js index 08259e02ca..e3ad7e3a67 100644 --- a/core/js/eventsource.js +++ b/core/js/eventsource.js @@ -40,6 +40,7 @@ OC.EventSource=function(src,data){ dataStr+=name+'='+encodeURIComponent(data[name])+'&'; } } + dataStr+='requesttoken='+OC.EventSource.requesttoken; if(!this.useFallBack && typeof EventSource !='undefined'){ this.source=new EventSource(src+'?'+dataStr); this.source.onmessage=function(e){ diff --git a/core/js/js.js b/core/js/js.js index dec6ea047c..86e802cd34 100644 --- a/core/js/js.js +++ b/core/js/js.js @@ -31,7 +31,7 @@ t.cache={}; OC={ webroot:oc_webroot, - appswebroot:oc_appswebroot, + appswebroots:oc_appswebroots, currentUser:(typeof oc_current_user!=='undefined')?oc_current_user:false, coreApps:['', 'admin','log','search','settings','core','3rdparty'], /** @@ -63,13 +63,12 @@ OC={ link+= file; } }else if(file.substring(file.length-3) != 'php' && !isCore){ - link=OC.appswebroot; - link+='/'; - link+='apps/'; - link+=app+'/'; + link=OC.appswebroots[app]; if(type){ - link+=type+'/'; + link+= '/'+type+'/'; } + if(link.substring(link.length-1) != '/') + link+='/'; link+=file; }else{ link+='/'; @@ -83,7 +82,7 @@ OC={ if(type){ link+=type+'/'; } - link+=file; + link+=file; } return link; }, @@ -92,9 +91,9 @@ OC={ * @param app the app id to which the image belongs * @param file the name of the image file * @return string - * + * * if no extension is given for the image, it will automatically decide between .png and .svg based on what the browser supports - */ + */ imagePath:function(app,file){ if(file.indexOf('.')==-1){//if no extension is given, use png or svg depending on browser support file+=(SVGSupport())?'.svg':'.png'; @@ -106,7 +105,7 @@ OC={ * @param app the app id to which the script belongs * @param script the filename of the script * @param ready event handeler to be called when the script is loaded - * + * * if the script is already loaded, the event handeler will be called directly */ addScript:function(app,script,ready){ @@ -151,12 +150,68 @@ OC={ } }, dialogs:OCdialogs, - mtime2date:function(mtime) { - mtime = parseInt(mtime); - var date = new Date(1000*mtime); - var ret = date.getDate()+'.'+(date.getMonth()+1)+'.'+date.getFullYear()+', '+date.getHours()+':'+date.getMinutes(); - return ret; - } + mtime2date:function(mtime) { + mtime = parseInt(mtime); + var date = new Date(1000*mtime); + var ret = date.getDate()+'.'+(date.getMonth()+1)+'.'+date.getFullYear()+', '+date.getHours()+':'+date.getMinutes(); + return ret; + }, + /** + * Opens a popup with the setting for an app. + * @param appid String. The ID of the app e.g. 'calendar', 'contacts' or 'files'. + * @param loadJS boolean or String. If true 'js/settings.js' is loaded. If it's a string + * it will attempt to load a script by that name in the 'js' directory. + * @param cache boolean. If true the javascript file won't be forced refreshed. Defaults to true. + * @param scriptName String. The name of the PHP file to load. Defaults to 'settings.php' in + * the root of the app directory hierarchy. + */ + appSettings:function(args) { + if(typeof args === 'undefined' || typeof args.appid === 'undefined') { + throw { name: 'MissingParameter', message: 'The parameter appid is missing' }; + } + var props = {scriptName:'settings.php', cache:true}; + $.extend(props, args); + var settings = $('#appsettings'); + if(settings.length == 0) { + throw { name: 'MissingDOMElement', message: 'There has be be an element with id "appsettings" for the popup to show.' }; + } + var popup = $('#appsettings_popup'); + if(popup.length == 0) { + $('body').prepend(''); + popup = $('#appsettings_popup'); + popup.addClass(settings.hasClass('topright') ? 'topright' : 'bottomleft'); + } + if(popup.is(':visible')) { + popup.hide().remove(); + } else { + var arrowclass = settings.hasClass('topright') ? 'up' : 'left'; + var jqxhr = $.get(OC.filePath(props.appid, '', props.scriptName), function(data) { + popup.html(data).ready(function() { + popup.prepend('

        '+t('core', 'Settings')+'

        ').show(); + popup.find('.close').bind('click', function() { + popup.remove(); + }) + if(typeof props.loadJS !== 'undefined') { + var scriptname; + if(props.loadJS === true) { + scriptname = 'settings.js'; + } else if(typeof props.loadJS === 'string') { + scriptname = props.loadJS; + } else { + throw { name: 'InvalidParameter', message: 'The "loadJS" parameter must be either boolean or a string.' }; + } + if(props.cache) { + $.ajaxSetup({cache: true}); + } + $.getScript(OC.filePath(props.appid, 'js', scriptname)) + .fail(function(jqxhr, settings, e) { + throw e; + }); + } + }).show(); + }, 'html'); + } + } }; OC.search.customResults={}; OC.search.currentResult=-1; @@ -203,7 +258,7 @@ if (!Array.prototype.filter) { var len = this.length >>> 0; if (typeof fun != "function") throw new TypeError(); - + var res = []; var thisp = arguments[1]; for (var i = 0; i < len; i++) { @@ -223,14 +278,14 @@ if (!Array.prototype.indexOf){ Array.prototype.indexOf = function(elt /*, from*/) { var len = this.length; - + var from = Number(arguments[1]) || 0; from = (from < 0) ? Math.ceil(from) : Math.floor(from); if (from < 0) from += len; - + for (; from < len; from++) { if (from in this && @@ -307,7 +362,7 @@ function replaceSVG(){ /** * prototypal inharitence functions - * + * * usage: * MySubObject=object(MyObject) */ @@ -353,7 +408,7 @@ $(document).ready(function(){ fillWindow($('#rightcontent')); }); $(window).trigger('resize'); - + if(!SVGSupport()){ //replace all svg images with png images for browser that dont support svg replaceSVG(); }else{ @@ -396,7 +451,7 @@ $(document).ready(function(){ } }); - // 'show password' checkbox + // 'show password' checkbox $('#pass2').showPassword(); //use infield labels @@ -420,8 +475,9 @@ $(document).ready(function(){ } } // hide log in button etc. when form fields not filled - checkShowCredentials(); - $('input#user, input#password').keyup(checkShowCredentials); + // commented out due to some browsers having issues with it + // checkShowCredentials(); + // $('input#user, input#password').keyup(checkShowCredentials); $('#settings #expand').keydown(function(event) { if (event.which == 13 || event.which == 32) { @@ -462,15 +518,15 @@ $(document).ready(function(){ if (!Array.prototype.map){ Array.prototype.map = function(fun /*, thisp */){ "use strict"; - + if (this === void 0 || this === null) throw new TypeError(); - + var t = Object(this); var len = t.length >>> 0; if (typeof fun !== "function") throw new TypeError(); - + var res = new Array(len); var thisp = arguments[1]; for (var i = 0; i < len; i++){ @@ -478,16 +534,16 @@ if (!Array.prototype.map){ res[i] = fun.call(thisp, t[i], i, t); } } - - return res; + + return res; }; } /** * Filter Jquery selector by attribute value **/ -$.fn.filterAttr = function(attr_name, attr_value) { - return this.filter(function() { return $(this).attr(attr_name) === attr_value; }); +$.fn.filterAttr = function(attr_name, attr_value) { + return this.filter(function() { return $(this).attr(attr_name) === attr_value; }); }; function humanFileSize(size) { diff --git a/core/js/share.js b/core/js/share.js new file mode 100644 index 0000000000..e765303a26 --- /dev/null +++ b/core/js/share.js @@ -0,0 +1,377 @@ +OC.Share={ + SHARE_TYPE_USER:0, + SHARE_TYPE_GROUP:1, + SHARE_TYPE_PRIVATE_LINK:3, + SHARE_TYPE_EMAIL:4, + PERMISSION_CREATE:4, + PERMISSION_READ:1, + PERMISSION_UPDATE:2, + PERMISSION_DELETE:8, + PERMISSION_SHARE:16, + itemShares:[], + statuses:[], + droppedDown:false, + loadIcons:function(itemType) { + // Load all share icons + $.get(OC.filePath('core', 'ajax', 'share.php'), { fetch: 'getItemsSharedStatuses', itemType: itemType }, function(result) { + if (result && result.status === 'success') { + $.each(result.data, function(item, hasPrivateLink) { + // Private links override shared in terms of icon display + if (itemType != 'file' && itemType != 'folder') { + if (hasPrivateLink) { + var image = OC.imagePath('core', 'actions/public'); + } else { + var image = OC.imagePath('core', 'actions/shared'); + } + $('a.share[data-item="'+item+'"]').css('background', 'url('+image+') no-repeat center'); + } + OC.Share.statuses[item] = hasPrivateLink; + }); + } + }); + }, + loadItem:function(itemType, itemSource) { + var data = ''; + if (typeof OC.Share.statuses[itemSource] === 'undefined') { + checkShares = false; + } else { + checkShares = true; + } + $.ajax({type: 'GET', url: OC.filePath('core', 'ajax', 'share.php'), data: { fetch: 'getItem', itemType: itemType, itemSource: itemSource, checkShares: checkShares }, async: false, success: function(result) { + if (result && result.status === 'success') { + data = result.data; + } else { + data = false; + } + }}); + return data; + }, + share:function(itemType, itemSource, shareType, shareWith, permissions, callback) { + $.post(OC.filePath('core', 'ajax', 'share.php'), { action: 'share', itemType: itemType, itemSource: itemSource, shareType: shareType, shareWith: shareWith, permissions: permissions }, function(result) { + if (result && result.status === 'success') { + if (callback) { + callback(result.data); + } + } else { + OC.dialogs.alert(result.data.message, 'Error while sharing'); + } + }); + }, + unshare:function(itemType, itemSource, shareType, shareWith, callback) { + $.post(OC.filePath('core', 'ajax', 'share.php'), { action: 'unshare', itemType: itemType, itemSource: itemSource, shareType: shareType, shareWith: shareWith }, function(result) { + if (result && result.status === 'success') { + if (callback) { + callback(); + } + } else { + OC.dialogs.alert('Error', 'Error while unsharing'); + } + }); + }, + setPermissions:function(itemType, itemSource, shareType, shareWith, permissions) { + $.post(OC.filePath('core', 'ajax', 'share.php'), { action: 'setPermissions', itemType: itemType, itemSource: itemSource, shareType: shareType, shareWith: shareWith, permissions: permissions }, function(result) { + if (!result || result.status !== 'success') { + OC.dialogs.alert('Error', 'Error while changing permissions'); + } + }); + }, + showDropDown:function(itemType, itemSource, appendTo, privateLink, possiblePermissions) { + var data = OC.Share.loadItem(itemType, itemSource); + var html = ''; + $(html).appendTo(appendTo); + // Reset item shares + OC.Share.itemShares = []; + if (data.shares) { + $.each(data.shares, function(index, share) { + if (share.share_type == OC.Share.SHARE_TYPE_PRIVATE_LINK) { + OC.Share.showPrivateLink(item, share.share_with); + } else { + OC.Share.addShareWith(share.share_type, share.share_with, share.permissions, possiblePermissions); + + } + }); + } + $('#shareWith').autocomplete({minLength: 2, source: function(search, response) { +// if (cache[search.term]) { +// response(cache[search.term]); +// } else { + $.get(OC.filePath('core', 'ajax', 'share.php'), { fetch: 'getShareWith', search: search.term, itemShares: OC.Share.itemShares }, function(result) { + if (result.status == 'success' && result.data.length > 0) { + response(result.data); + } else { + // Suggest sharing via email if valid email address + var pattern = new RegExp(/^[+a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/i); + if (pattern.test(search.term)) { + response([{label: 'Share via email: '+search.term, value: {shareType: OC.Share.SHARE_TYPE_EMAIL, shareWith: search.term}}]); + } else { + response(['No people found']); + } + } + }); +// } + }, + focus: function(event, focused) { + event.preventDefault(); + }, + select: function(event, selected) { + var shareType = selected.item.value.shareType; + var shareWith = selected.item.value.shareWith; + $(this).val(shareWith); + // Default permissions are Read and Share + var permissions = OC.Share.PERMISSION_READ | OC.Share.PERMISSION_SHARE; + OC.Share.share($('#dropdown').data('item-type'), $('#dropdown').data('item-source'), shareType, shareWith, permissions, function() { + OC.Share.addShareWith(shareType, shareWith, permissions, possiblePermissions); + $('#shareWith').val(''); + }); + return false; + } + }); + $('#dropdown').show('blind', function() { + OC.Share.droppedDown = true; + }); + $('#shareWith').focus(); + }, + hideDropDown:function(callback) { + $('#dropdown').hide('blind', function() { + OC.Share.droppedDown = false; + $('#dropdown').remove(); + if (typeof FileActions !== 'undefined') { + $('tr').removeClass('mouseOver'); + } + if (callback) { + callback.call(); + } + }); + }, + addShareWith:function(shareType, shareWith, permissions, possiblePermissions) { + if (!OC.Share.itemShares[shareType]) { + OC.Share.itemShares[shareType] = []; + } + OC.Share.itemShares[shareType].push(shareWith); + var editChecked = createChecked = updateChecked = deleteChecked = shareChecked = ''; + if (permissions & OC.Share.PERMISSION_CREATE) { + createChecked = 'checked="checked"'; + editChecked = 'checked="checked"'; + } + if (permissions & OC.Share.PERMISSION_UPDATE) { + updateChecked = 'checked="checked"'; + editChecked = 'checked="checked"'; + } + if (permissions & OC.Share.PERMISSION_DELETE) { + deleteChecked = 'checked="checked"'; + editChecked = 'checked="checked"'; + } + if (permissions & OC.Share.PERMISSION_SHARE) { + shareChecked = 'checked="checked"'; + } + var html = '
      • '; + html += shareWith; + if (possiblePermissions & OC.Share.PERMISSION_CREATE || possiblePermissions & OC.Share.PERMISSION_UPDATE || possiblePermissions & OC.Share.PERMISSION_DELETE) { + if (editChecked == '') { + html += '
      • '; + $(html).appendTo('#shareWithList'); + + }, + showPrivateLink:function(item, token) { + $('#privateLinkCheckbox').attr('checked', true); + var link = parent.location.protocol+'//'+location.host+OC.linkTo('', 'public.php')+'?service=files&token='+token; + if (token.indexOf('&path=') == -1) { + link += '&file=' + encodeURIComponent(item).replace(/%2F/g, '/'); + } else { + // Disable checkbox if inside a shared parent folder + $('#privateLinkCheckbox').attr('disabled', 'true'); + } + $('#privateLinkText').val(link); + $('#privateLinkText').show('blind', function() { + $('#privateLinkText').after('
        '); + $('#email').show(); + $('#emailButton').show(); + }); + }, + hidePrivateLink:function() { + $('#privateLinkText').hide('blind'); + $('#emailBreak').remove(); + $('#email').hide(); + $('#emailButton').hide(); + }, + emailPrivateLink:function() { + var link = $('#privateLinkText').val(); + var file = link.substr(link.lastIndexOf('/') + 1).replace(/%20/g, ' '); + $.post(OC.filePath('files_sharing', 'ajax', 'email.php'), { toaddress: $('#email').val(), link: link, file: file } ); + $('#email').css('font-weight', 'bold'); + $('#email').animate({ fontWeight: 'normal' }, 2000, function() { + $(this).val(''); + }).val('Email sent'); + }, + dirname:function(path) { + return path.replace(/\\/g,'/').replace(/\/[^\/]*$/, ''); + } +} + +$(document).ready(function() { + + $('a.share').live('click', function(event) { + event.stopPropagation(); + if ($(this).data('item-type') !== undefined && $(this).data('item') !== undefined) { + var itemType = $(this).data('item-type'); + var itemSource = $(this).data('item'); + var appendTo = $(this).parent().parent(); + var privateLink = false; + var possiblePermissions = $(this).data('possible-permissions'); + if ($(this).data('private-link') !== undefined && $(this).data('private-link') == true) { + privateLink = true; + } + if (OC.Share.droppedDown) { + if (itemSource != $('#dropdown').data('item')) { + OC.Share.hideDropDown(function () { + OC.Share.showDropDown(itemType, itemSource, appendTo, privateLink, possiblePermissions); + }); + } else { + OC.Share.hideDropDown(); + } + } else { + OC.Share.showDropDown(itemType, itemSource, appendTo, privateLink, possiblePermissions); + } + } + }); + + $(this).click(function(event) { + if (OC.Share.droppedDown && !($(event.target).hasClass('drop')) && $('#dropdown').has(event.target).length === 0) { + OC.Share.hideDropDown(); + } + }); + + $('#shareWithList li').live('mouseenter', function(event) { + // Show permissions and unshare button + $(':hidden', this).filter(':not(.cruds)').show(); + }); + + $('#shareWithList li').live('mouseleave', function(event) { + // Hide permissions and unshare button + if (!$('.cruds', this).is(':visible')) { + $('a', this).hide(); + if (!$('input[name="edit"]', this).is(':checked')) { + $('input:[type=checkbox]', this).hide(); + $('label', this).hide(); + } + } else { + $('a.unshare', this).hide(); + } + }); + + $('.showCruds').live('click', function() { + $(this).parent().find('.cruds').toggle(); + }); + + $('.unshare').live('click', function() { + var li = $(this).parent(); + var shareType = $(li).data('share-type'); + var shareWith = $(li).data('share-with'); + OC.Share.unshare($('#dropdown').data('item-type'), $('#dropdown').data('item-source'), shareType, shareWith, function() { + $(li).remove(); + var index = OC.Share.itemShares[shareType].indexOf(shareWith); + OC.Share.itemShares[shareType].splice(index, 1); + }); + }); + + $('.permissions').live('change', function() { + if ($(this).attr('name') == 'edit') { + var li = $(this).parent().parent() + var checkboxes = $('.permissions', li); + var checked = $(this).is(':checked'); + // Check/uncheck Create, Update, and Delete checkboxes if Edit is checked/unck + $(checkboxes).filter('input[name="create"]').attr('checked', checked); + $(checkboxes).filter('input[name="update"]').attr('checked', checked); + $(checkboxes).filter('input[name="delete"]').attr('checked', checked); + } else { + var li = $(this).parent().parent().parent(); + var checkboxes = $('.permissions', li); + // Uncheck Edit if Create, Update, and Delete are not checked + if (!$(this).is(':checked') && !$(checkboxes).filter('input[name="create"]').is(':checked') && !$(checkboxes).filter('input[name="update"]').is(':checked') && !$(checkboxes).filter('input[name="delete"]').is(':checked')) { + $(checkboxes).filter('input[name="edit"]').attr('checked', false); + // Check Edit if Create, Update, or Delete is checked + } else if (($(this).attr('name') == 'create' || $(this).attr('name') == 'update' || $(this).attr('name') == 'delete')) { + $(checkboxes).filter('input[name="edit"]').attr('checked', true); + } + } + var permissions = OC.Share.PERMISSION_READ; + $(checkboxes).filter(':not(input[name="edit"])').filter(':checked').each(function(index, checkbox) { + permissions |= $(checkbox).data('permissions'); + }); + OC.Share.setPermissions($('#dropdown').data('item-type'), $('#dropdown').data('item-source'), $(li).data('share-type'), $(li).data('share-with'), permissions); + }); + + $('#privateLinkCheckbox').live('change', function() { + var itemType = $('#dropdown').data('item-type'); + var item = $('#dropdown').data('item'); + if (this.checked) { + // Create a private link + OC.Share.share(itemType, item, OC.Share.SHARE_TYPE_PRIVATE_LINK, 0, 0, function(token) { + OC.Share.showPrivateLink(item, 'foo'); + // Change icon + OC.Share.icons[item] = OC.imagePath('core', 'actions/public'); + }); + } else { + // Delete private link + OC.Share.unshare(item, 'public', function() { + OC.Share.hidePrivateLink(); + // Change icon + if (OC.Share.itemUsers || OC.Share.itemGroups) { + OC.Share.icons[item] = OC.imagePath('core', 'actions/shared'); + } else { + OC.Share.icons[item] = OC.imagePath('core', 'actions/share'); + } + }); + } + }); + + $('#privateLinkText').live('click', function() { + $(this).focus(); + $(this).select(); + }); + + $('#emailPrivateLink').live('submit', function() { + OC.Share.emailPrivateLink(); + }); +}); diff --git a/core/l10n/ar.php b/core/l10n/ar.php index 8a6ba6e9a7..4b694f33bd 100644 --- a/core/l10n/ar.php +++ b/core/l10n/ar.php @@ -1,5 +1,5 @@ "استرجاع كلمة السر", +"Settings" => "تعديلات", "Use the following link to reset your password: {link}" => "استخدم هذه الوصلة لاسترجاع كلمة السر: {link}", "You will receive a link to reset your password via Email." => "سوف نرسل لك بريد يحتوي على وصلة لتجديد كلمة السر.", "Requested" => "تم طلب", @@ -29,7 +29,6 @@ "Finish setup" => "انهاء التعديلات", "web services under your control" => "خدمات الوب تحت تصرفك", "Log out" => "الخروج", -"Settings" => "تعديلات", "Lost your password?" => "هل نسيت كلمة السر؟", "remember" => "تذكر", "Log in" => "أدخل", diff --git a/core/l10n/bg_BG.php b/core/l10n/bg_BG.php index 46047a3535..19b32a700b 100644 --- a/core/l10n/bg_BG.php +++ b/core/l10n/bg_BG.php @@ -1,4 +1,24 @@ "Категорията вече съществува:", +"Settings" => "Настройки", +"January" => "Януари", +"February" => "Февруари", +"March" => "Март", +"April" => "Април", +"May" => "Май", +"June" => "Юни", +"July" => "Юли", +"August" => "Август", +"September" => "Септември", +"October" => "Октомври", +"November" => "Ноември", +"December" => "Декември", +"Cancel" => "Отказ", +"No" => "Не", +"Yes" => "Да", +"Ok" => "Добре", +"No categories selected for deletion." => "Няма избрани категории за изтриване", +"Error" => "Грешка", "You will receive a link to reset your password via Email." => "Ще получите връзка за нулиране на паролата Ви.", "Requested" => "Заявено", "Login failed!" => "Входа пропадна!", @@ -12,7 +32,10 @@ "Apps" => "Програми", "Admin" => "Админ", "Help" => "Помощ", +"Access forbidden" => "Достъпът е забранен", "Cloud not found" => "облакът не намерен", +"Edit categories" => "Редактиране на категориите", +"Add" => "Добавяне", "Create an admin account" => "Създаване на админ профил", "Password" => "Парола", "Advanced" => "Разширено", @@ -25,9 +48,9 @@ "Database host" => "Хост за базата", "Finish setup" => "Завършване на настройките", "Log out" => "Изход", -"Settings" => "Настройки", "Lost your password?" => "Забравена парола?", "remember" => "запомни", +"Log in" => "Вход", "You are logged out." => "Вие излязохте.", "prev" => "пред.", "next" => "следващо" diff --git a/core/l10n/ca.php b/core/l10n/ca.php index baeb8bd55b..9be2fe6adf 100644 --- a/core/l10n/ca.php +++ b/core/l10n/ca.php @@ -2,7 +2,26 @@ "Application name not provided." => "No s'ha facilitat cap nom per l'aplicació.", "No category to add?" => "No voleu afegir cap categoria?", "This category already exists: " => "Aquesta categoria ja existeix:", -"Owncloud password reset" => "Restableix la contrasenya d'Owncloud", +"ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=" => "ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=", +"Settings" => "Arranjament", +"January" => "Gener", +"February" => "Febrer", +"March" => "Març", +"April" => "Abril", +"May" => "Maig", +"June" => "Juny", +"July" => "Juliol", +"August" => "Agost", +"September" => "Setembre", +"October" => "Octubre", +"November" => "Novembre", +"December" => "Desembre", +"Cancel" => "Cancel·la", +"No" => "No", +"Yes" => "Sí", +"Ok" => "D'acord", +"No categories selected for deletion." => "No hi ha categories per eliminar.", +"Error" => "Error", "ownCloud password reset" => "estableix de nou la contrasenya Owncloud", "Use the following link to reset your password: {link}" => "Useu l'enllaç següent per restablir la contrasenya: {link}", "You will receive a link to reset your password via Email." => "Rebreu un enllaç al correu electrònic per reiniciar la contrasenya.", @@ -36,7 +55,6 @@ "Finish setup" => "Acaba la configuració", "web services under your control" => "controleu els vostres serveis web", "Log out" => "Surt", -"Settings" => "Arranjament", "Lost your password?" => "Heu perdut la contrasenya?", "remember" => "recorda'm", "Log in" => "Inici de sessió", diff --git a/core/l10n/cs_CZ.php b/core/l10n/cs_CZ.php index 4ad9508f81..7daeb52e63 100644 --- a/core/l10n/cs_CZ.php +++ b/core/l10n/cs_CZ.php @@ -2,7 +2,26 @@ "Application name not provided." => "Jméno aplikace nezadáno.", "No category to add?" => "Žádná kategorie k přidání?", "This category already exists: " => "Tato kategorie již existuje:", -"Owncloud password reset" => "Reset hesla Owncloud", +"ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=" => "ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=", +"Settings" => "Nastavení", +"January" => "Leden", +"February" => "Únor", +"March" => "Březen", +"April" => "Duben", +"May" => "Květen", +"June" => "Červen", +"July" => "Červenec", +"August" => "Srpen", +"September" => "Září", +"October" => "Říjen", +"November" => "Listopad", +"December" => "Prosinec", +"Cancel" => "Zrušit", +"No" => "Ne", +"Yes" => "Ano", +"Ok" => "Budiž", +"No categories selected for deletion." => "Žádné kategorie nebyly vybrány ke smazání.", +"Error" => "Chyba", "ownCloud password reset" => "Reset hesla pro ownCloud", "Use the following link to reset your password: {link}" => "Heslo vyresetujete použitím následujícího odkazu: {link}", "You will receive a link to reset your password via Email." => "Bude Vám zaslán odkaz pro obnovu hesla", @@ -36,7 +55,6 @@ "Finish setup" => "Dokončit instalaci", "web services under your control" => "webové služby pod Vaší kontrolou", "Log out" => "Odhlásit se", -"Settings" => "Nastavení", "Lost your password?" => "Zapomenuté heslo?", "remember" => "zapamatovat si", "Log in" => "Login", diff --git a/core/l10n/da.php b/core/l10n/da.php index f7b5b48f6f..5e62b5b95d 100644 --- a/core/l10n/da.php +++ b/core/l10n/da.php @@ -2,7 +2,26 @@ "Application name not provided." => "Applikationens navn ikke medsendt", "No category to add?" => "Ingen kategori at tilføje?", "This category already exists: " => "Denne kategori eksisterer allerede: ", -"Owncloud password reset" => "Nulstil adgangskode til Owncloud", +"ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=" => "ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=", +"Settings" => "Indstillinger", +"January" => "Januar", +"February" => "Februar", +"March" => "Marts", +"April" => "April", +"May" => "Maj", +"June" => "Juni", +"July" => "Juli", +"August" => "August", +"September" => "September", +"October" => "Oktober", +"November" => "November", +"December" => "December", +"Cancel" => "Fortryd", +"No" => "Nej", +"Yes" => "Ja", +"Ok" => "OK", +"No categories selected for deletion." => "Ingen kategorier valgt", +"Error" => "Fejl", "ownCloud password reset" => "Nulstil ownCloud kodeord", "Use the following link to reset your password: {link}" => "Anvend følgende link til at nulstille din adgangskode: {link}", "You will receive a link to reset your password via Email." => "Du vil modtage et link til at nulstille dit kodeord via email.", @@ -36,7 +55,6 @@ "Finish setup" => "Afslut opsætning", "web services under your control" => "Webtjenester under din kontrol", "Log out" => "Log ud", -"Settings" => "Indstillinger", "Lost your password?" => "Mistet dit kodeord?", "remember" => "husk", "Log in" => "Log ind", diff --git a/core/l10n/de.php b/core/l10n/de.php index 9fcd5c8496..9ed2d61782 100644 --- a/core/l10n/de.php +++ b/core/l10n/de.php @@ -2,45 +2,63 @@ "Application name not provided." => "Applikationsname nicht angegeben", "No category to add?" => "Keine Kategorie hinzuzufügen?", "This category already exists: " => "Kategorie existiert bereits:", -"Owncloud password reset" => "ownCloud Passwort zurücksetzen", -"ownCloud password reset" => "ownCloud Passwort zurücksetzen", -"Use the following link to reset your password: {link}" => "Nutze folgenden Link, um dein Passwort zurückzusetzen: {link}", +"ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=" => "ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=", +"Settings" => "Einstellungen", +"January" => "Januar", +"February" => "Februar", +"March" => "März", +"April" => "April", +"May" => "Mai", +"June" => "Juni", +"July" => "Juli", +"August" => "August", +"September" => "September", +"October" => "Oktober", +"November" => "November", +"December" => "Dezember", +"Cancel" => "Abbrechen", +"No" => "Nein", +"Yes" => "Ja", +"Ok" => "OK", +"No categories selected for deletion." => "Keine Kategorien zum Löschen angegeben.", +"Error" => "Fehler", +"ownCloud password reset" => "ownCloud-Passwort zurücksetzen", +"Use the following link to reset your password: {link}" => "Nutzen Sie den nachfolgenden Link, um Ihr Passwort zurückzusetzen: {link}", "You will receive a link to reset your password via Email." => "Sie erhalten einen Link, um Ihr Passwort per E-Mail zurückzusetzen.", "Requested" => "Angefragt", "Login failed!" => "Login fehlgeschlagen!", -"Username" => "Nutzername", +"Username" => "Benutzername", "Request reset" => "Anfrage zurückgesetzt", "Your password was reset" => "Ihr Passwort wurde zurückgesetzt.", "To login page" => "Zur Login-Seite", "New password" => "Neues Passwort", "Reset password" => "Passwort zurücksetzen", "Personal" => "Persönlich", -"Users" => "Nutzer", +"Users" => "Benutzer", "Apps" => "Anwendungen", -"Admin" => "Verwaltung", +"Admin" => "Admin", "Help" => "Hilfe", -"Access forbidden" => "Zugang verboten", -"Cloud not found" => "Cloud nicht verfügbar", -"Edit categories" => "Kategorien ändern", +"Access forbidden" => "Zugriff verboten", +"Cloud not found" => "Cloud nicht gefunden", +"Edit categories" => "Kategorien bearbeiten", "Add" => "Hinzufügen", -"Create an admin account" => "Admin-Konto anlegen", +"Create an admin account" => "Administrator-Konto anlegen", "Password" => "Passwort", "Advanced" => "Erweitert", "Data folder" => "Datenverzeichnis", "Configure the database" => "Datenbank einrichten", "will be used" => "wird genutzt", -"Database user" => "Datenbanknutzer", -"Database password" => "Datenbankpasswort", -"Database name" => "Datenbankname", +"Database user" => "Datenbank-Benutzer", +"Database password" => "Datenbank-Passwort", +"Database name" => "Datenbank-Name", "Database host" => "Datenbank-Host", "Finish setup" => "Installation abschließen", -"web services under your control" => "web services under your control", +"web services under your control" => "Web-Services unter Ihrer Kontrolle", "Log out" => "Abmelden", -"Settings" => "Einstellungen", "Lost your password?" => "Passwort vergessen?", "remember" => "merken", "Log in" => "Einloggen", -"You are logged out." => "Abgemeldet", +"You are logged out." => "Sie wurden abgemeldet.", "prev" => "Zurück", "next" => "Weiter" ); diff --git a/core/l10n/el.php b/core/l10n/el.php index 1adf14af96..d8f32fb51e 100644 --- a/core/l10n/el.php +++ b/core/l10n/el.php @@ -2,7 +2,26 @@ "Application name not provided." => "Δε προσδιορίστηκε όνομα εφαρμογής", "No category to add?" => "Δεν έχετε να προστέσθέσεται μια κα", "This category already exists: " => "Αυτή η κατηγορία υπάρχει ήδη", -"Owncloud password reset" => "Επανέκδοση κωδικού για το Owncloud", +"ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=" => "ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=", +"Settings" => "Ρυθμίσεις", +"January" => "Ιανουάριος", +"February" => "Φεβρουάριος", +"March" => "Μάρτιος", +"April" => "Απρίλιος", +"May" => "Μάϊος", +"June" => "Ιούνιος", +"July" => "Ιούλιος", +"August" => "Αύγουστος", +"September" => "Σεπτέμβριος", +"October" => "Οκτώβριος", +"November" => "Νοέμβριος", +"December" => "Δεκέμβριος", +"Cancel" => "Ακύρωση", +"No" => "Όχι", +"Yes" => "Ναι", +"Ok" => "Οκ", +"No categories selected for deletion." => "Δεν επιλέχτηκαν κατηγορίες για διαγραφή", +"Error" => "Σφάλμα", "ownCloud password reset" => "Επαναφορά κωδικού ownCloud", "Use the following link to reset your password: {link}" => "Χρησιμοποιήστε τον ακόλουθο σύνδεσμο για να επανεκδόσετε τον κωδικό: {link}", "You will receive a link to reset your password via Email." => "Θα λάβετε ένα σύνδεσμο για να επαναφέρετε τον κωδικό πρόσβασής σας μέσω ηλεκτρονικού ταχυδρομείου.", @@ -36,7 +55,6 @@ "Finish setup" => "Ολοκλήρωση εγκατάστασης", "web services under your control" => "Υπηρεσίες web υπό τον έλεγχό σας", "Log out" => "Αποσύνδεση", -"Settings" => "Ρυθμίσεις", "Lost your password?" => "Ξεχάσατε τον κωδικό σας;", "remember" => "να με θυμάσαι", "Log in" => "Είσοδος", diff --git a/core/l10n/eo.php b/core/l10n/eo.php index 0ae2aeac69..f5e5ca9d0e 100644 --- a/core/l10n/eo.php +++ b/core/l10n/eo.php @@ -1,5 +1,28 @@ "La pasvorto de Owncloud estas restarigita", +"Application name not provided." => "Nomo de aplikaĵo ne proviziiĝis.", +"No category to add?" => "Ĉu neniu kategorio estas aldonota?", +"This category already exists: " => "Ĉi tiu kategorio jam ekzistas: ", +"ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=" => "ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=", +"Settings" => "Agordo", +"January" => "Januaro", +"February" => "Februaro", +"March" => "Marto", +"April" => "Aprilo", +"May" => "Majo", +"June" => "Junio", +"July" => "Julio", +"August" => "Aŭgusto", +"September" => "Septembro", +"October" => "Oktobro", +"November" => "Novembro", +"December" => "Decembro", +"Cancel" => "Nuligi", +"No" => "Ne", +"Yes" => "Jes", +"Ok" => "Akcepti", +"No categories selected for deletion." => "Neniu kategorio elektiĝis por forigo.", +"Error" => "Eraro", +"ownCloud password reset" => "La pasvorto de ownCloud restariĝis.", "Use the following link to reset your password: {link}" => "Uzu la jenan ligilon por restarigi vian pasvorton: {link}", "You will receive a link to reset your password via Email." => "Vi ricevos ligilon retpoŝte por rekomencigi vian pasvorton.", "Requested" => "Petita", @@ -15,7 +38,10 @@ "Apps" => "Aplikaĵoj", "Admin" => "Administranto", "Help" => "Helpo", +"Access forbidden" => "Aliro estas malpermesata", "Cloud not found" => "La nubo ne estas trovita", +"Edit categories" => "Redakti kategoriojn", +"Add" => "Aldoni", "Create an admin account" => "Krei administran konton", "Password" => "Pasvorto", "Advanced" => "Progresinta", @@ -29,7 +55,6 @@ "Finish setup" => "Fini la instalon", "web services under your control" => "TTT-servoj sub via kontrolo", "Log out" => "Elsaluti", -"Settings" => "Agordo", "Lost your password?" => "Ĉu vi perdis vian pasvorton?", "remember" => "memori", "Log in" => "Ensaluti", diff --git a/core/l10n/es.php b/core/l10n/es.php index 2d6068ed5f..8766228ba8 100644 --- a/core/l10n/es.php +++ b/core/l10n/es.php @@ -1,8 +1,27 @@ "Nombre de la aplicación no provisto.", -"No category to add?" => "¿Ninguna categoría para agregar?", +"No category to add?" => "¿Ninguna categoría para añadir?", "This category already exists: " => "Esta categoría ya existe: ", -"Owncloud password reset" => "Restablecer contraseña de ownCloud", +"ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=" => "ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=", +"Settings" => "Ajustes", +"January" => "Enero", +"February" => "Febrero", +"March" => "Marzo", +"April" => "Abril", +"May" => "Mayo", +"June" => "Junio", +"July" => "Julio", +"August" => "Agosto", +"September" => "Septiembre", +"October" => "Octubre", +"November" => "Noviembre", +"December" => "Diciembre", +"Cancel" => "Cancelar", +"No" => "No", +"Yes" => "Sí", +"Ok" => "Aceptar", +"No categories selected for deletion." => "No hay categorías seleccionadas para borrar.", +"Error" => "Fallo", "ownCloud password reset" => "Reiniciar contraseña de ownCloud", "Use the following link to reset your password: {link}" => "Utiliza el siguiente enlace para restablecer tu contraseña: {link}", "You will receive a link to reset your password via Email." => "Recibirás un enlace por correo electrónico para restablecer tu contraseña", @@ -36,7 +55,6 @@ "Finish setup" => "Completar la instalación", "web services under your control" => "servicios web bajo tu control", "Log out" => "Salir", -"Settings" => "Ajustes", "Lost your password?" => "¿Has perdido tu contraseña?", "remember" => "recuérdame", "Log in" => "Entrar", diff --git a/core/l10n/et_EE.php b/core/l10n/et_EE.php index 7ecfb278af..734021605c 100644 --- a/core/l10n/et_EE.php +++ b/core/l10n/et_EE.php @@ -2,7 +2,26 @@ "Application name not provided." => "Rakenduse nime pole sisestatud.", "No category to add?" => "Pole kategooriat, mida lisada?", "This category already exists: " => "See kategooria on juba olemas: ", -"Owncloud password reset" => "Owncloud parooli taastamine", +"ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=" => "ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=", +"Settings" => "Seaded", +"January" => "Jaanuar", +"February" => "Veebruar", +"March" => "Märts", +"April" => "Aprill", +"May" => "Mai", +"June" => "Juuni", +"July" => "Juuli", +"August" => "August", +"September" => "September", +"October" => "Oktoober", +"November" => "November", +"December" => "Detsember", +"Cancel" => "Loobu", +"No" => "Ei", +"Yes" => "Jah", +"Ok" => "Ok", +"No categories selected for deletion." => "Kustutamiseks pole kategooriat valitud.", +"Error" => "Viga", "ownCloud password reset" => "ownCloud parooli taastamine", "Use the following link to reset your password: {link}" => "Kasuta järgnevat linki oma parooli taastamiseks: {link}", "You will receive a link to reset your password via Email." => "Sinu parooli taastamise link saadetakse sulle e-postile.", @@ -36,7 +55,6 @@ "Finish setup" => "Lõpeta seadistamine", "web services under your control" => "veebiteenused sinu kontrolli all", "Log out" => "Logi välja", -"Settings" => "Seaded", "Lost your password?" => "Kaotasid oma parooli?", "remember" => "pea meeles", "Log in" => "Logi sisse", diff --git a/core/l10n/eu.php b/core/l10n/eu.php index 3159350183..2e5a2c00e2 100644 --- a/core/l10n/eu.php +++ b/core/l10n/eu.php @@ -2,7 +2,26 @@ "Application name not provided." => "Aplikazioaren izena falta da", "No category to add?" => "Ez dago gehitzeko kategoriarik?", "This category already exists: " => "Kategoria hau dagoeneko existitzen da:", -"Owncloud password reset" => "Owncloudeko pasahitza berrezarri", +"ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=" => "ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=", +"Settings" => "Ezarpenak", +"January" => "Urtarrila", +"February" => "Otsaila", +"March" => "Martxoa", +"April" => "Apirila", +"May" => "Maiatza", +"June" => "Ekaina", +"July" => "Uztaila", +"August" => "Abuztua", +"September" => "Iraila", +"October" => "Urria", +"November" => "Azaroa", +"December" => "Abendua", +"Cancel" => "Ezeztatu", +"No" => "Ez", +"Yes" => "Bai", +"Ok" => "Ados", +"No categories selected for deletion." => "Ez da ezabatzeko kategoriarik hautatu.", +"Error" => "Errorea", "ownCloud password reset" => "ownCloud-en pasahitza berrezarri", "Use the following link to reset your password: {link}" => "Eribili hurrengo lotura zure pasahitza berrezartzeko: {link}", "You will receive a link to reset your password via Email." => "Zure pashitza berrezartzeko lotura bat jasoko duzu Epostaren bidez.", @@ -36,7 +55,6 @@ "Finish setup" => "Bukatu konfigurazioa", "web services under your control" => "web zerbitzuak zure kontrolpean", "Log out" => "Saioa bukatu", -"Settings" => "Ezarpenak", "Lost your password?" => "Galdu duzu pasahitza?", "remember" => "gogoratu", "Log in" => "Hasi saioa", diff --git a/core/l10n/fa.php b/core/l10n/fa.php index e3cc3feddd..5fe98629ba 100644 --- a/core/l10n/fa.php +++ b/core/l10n/fa.php @@ -2,7 +2,26 @@ "Application name not provided." => "نام برنامه پیدا نشد", "No category to add?" => "آیا گروه دیگری برای افزودن ندارید", "This category already exists: " => "این گروه از قبل اضافه شده", -"Owncloud password reset" => "گذرواژه ابرهای شما تغییرکرد", +"ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=" => "ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=", +"Settings" => "تنظیمات", +"January" => "ژانویه", +"February" => "فبریه", +"March" => "مارس", +"April" => "آوریل", +"May" => "می", +"June" => "ژوئن", +"July" => "جولای", +"August" => "آگوست", +"September" => "سپتامبر", +"October" => "اکتبر", +"November" => "نوامبر", +"December" => "دسامبر", +"Cancel" => "منصرف شدن", +"No" => "نه", +"Yes" => "بله", +"Ok" => "قبول", +"No categories selected for deletion." => "هیج دسته ای برای پاک شدن انتخاب نشده است", +"Error" => "خطا", "ownCloud password reset" => "پسورد ابرهای شما تغییرکرد", "Use the following link to reset your password: {link}" => "از لینک زیر جهت دوباره سازی پسورد استفاده کنید :\n{link}", "You will receive a link to reset your password via Email." => "شما یک نامه الکترونیکی حاوی یک لینک جهت بازسازی گذرواژه دریافت خواهید کرد.", @@ -36,7 +55,6 @@ "Finish setup" => "اتمام نصب", "web services under your control" => "سرویس وب تحت کنترل شما", "Log out" => "خروج", -"Settings" => "تنظیمات", "Lost your password?" => "آیا گذرواژه تان را به یاد نمی آورید؟", "remember" => "بیاد آوری", "Log in" => "ورود", diff --git a/core/l10n/fi_FI.php b/core/l10n/fi_FI.php index 6dc65ed495..64bb4dca63 100644 --- a/core/l10n/fi_FI.php +++ b/core/l10n/fi_FI.php @@ -2,7 +2,25 @@ "Application name not provided." => "Sovelluksen nimeä ei määritelty.", "No category to add?" => "Ei lisättävää luokkaa?", "This category already exists: " => "Tämä luokka on jo olemassa: ", -"Owncloud password reset" => "Owncloud-salasanan nollaus", +"Settings" => "Asetukset", +"January" => "Tammikuu", +"February" => "Helmikuu", +"March" => "Maaliskuu", +"April" => "Huhtikuu", +"May" => "Toukokuu", +"June" => "Kesäkuu", +"July" => "Heinäkuu", +"August" => "Elokuu", +"September" => "Syyskuu", +"October" => "Lokakuu", +"November" => "Marraskuu", +"December" => "Joulukuu", +"Cancel" => "Peru", +"No" => "Ei", +"Yes" => "Kyllä", +"Ok" => "Ok", +"No categories selected for deletion." => "Luokkia ei valittu poistettavaksi.", +"Error" => "Virhe", "ownCloud password reset" => "ownCloud-salasanan nollaus", "Use the following link to reset your password: {link}" => "Voit palauttaa salasanasi seuraavassa osoitteessa: {link}", "You will receive a link to reset your password via Email." => "Saat sähköpostitse linkin nollataksesi salasanan.", @@ -36,7 +54,6 @@ "Finish setup" => "Viimeistele asennus", "web services under your control" => "verkkopalvelut hallinnassasi", "Log out" => "Kirjaudu ulos", -"Settings" => "Asetukset", "Lost your password?" => "Unohditko salasanasi?", "remember" => "muista", "Log in" => "Kirjaudu sisään", diff --git a/core/l10n/fr.php b/core/l10n/fr.php index 8459fbbb66..43917917ad 100644 --- a/core/l10n/fr.php +++ b/core/l10n/fr.php @@ -2,7 +2,26 @@ "Application name not provided." => "Nom de l'application non fourni.", "No category to add?" => "Pas de catégorie à ajouter ?", "This category already exists: " => "Cette catégorie existe déjà : ", -"Owncloud password reset" => "Réinitialisation de votre mot de passe Owncloud", +"ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=" => "ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=", +"Settings" => "Paramètres", +"January" => "Janvier", +"February" => "Février", +"March" => "Mars", +"April" => "Avril", +"May" => "Mai", +"June" => "Juin", +"July" => "Juillet", +"August" => "Août", +"September" => "Septembre", +"October" => "Octobre", +"November" => "Novembre", +"December" => "Décembre", +"Cancel" => "Annulé", +"No" => "Non", +"Yes" => "Oui", +"Ok" => "Ok", +"No categories selected for deletion." => "Aucune catégorie sélectionnée pour suppression", +"Error" => "Erreur", "ownCloud password reset" => "Réinitialisation de votre mot de passe Owncloud", "Use the following link to reset your password: {link}" => "Utilisez le lien suivant pour réinitialiser votre mot de passe : {link}", "You will receive a link to reset your password via Email." => "Vous allez recevoir un e-mail contenant un lien pour réinitialiser votre mot de passe", @@ -29,14 +48,13 @@ "Data folder" => "Répertoire des données", "Configure the database" => "Configurer la base de données", "will be used" => "sera utilisé", -"Database user" => "Utilisateur de la base de données", +"Database user" => "Utilisateur pour la base de données", "Database password" => "Mot de passe de la base de données", "Database name" => "Nom de la base de données", "Database host" => "Serveur de la base de données", "Finish setup" => "Terminer l'installation", "web services under your control" => "services web sous votre contrôle", "Log out" => "Se déconnecter", -"Settings" => "Paramètres", "Lost your password?" => "Mot de passe perdu ?", "remember" => "se souvenir de moi", "Log in" => "Connexion", diff --git a/core/l10n/gl.php b/core/l10n/gl.php index 8d839a6ac6..eeff78826f 100644 --- a/core/l10n/gl.php +++ b/core/l10n/gl.php @@ -2,7 +2,26 @@ "Application name not provided." => "Non se indicou o nome do aplicativo.", "No category to add?" => "Sen categoría que engadir?", "This category already exists: " => "Esta categoría xa existe: ", -"Owncloud password reset" => "Restablecemento do contrasinal de Owncloud", +"ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=" => "ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=", +"Settings" => "Preferencias", +"January" => "Xaneiro", +"February" => "Febreiro", +"March" => "Marzo", +"April" => "Abril", +"May" => "Maio", +"June" => "Xuño", +"July" => "Xullo", +"August" => "Agosto", +"September" => "Setembro", +"October" => "Outubro", +"November" => "Novembro", +"December" => "Nadal", +"Cancel" => "Cancelar", +"No" => "Non", +"Yes" => "Si", +"Ok" => "Ok", +"No categories selected for deletion." => "Non hai categorías seleccionadas para eliminar.", +"Error" => "Erro", "ownCloud password reset" => "Restablecer contrasinal de ownCloud", "Use the following link to reset your password: {link}" => "Use a seguinte ligazón para restablecer o contrasinal: {link}", "You will receive a link to reset your password via Email." => "Recibirá unha ligazón por correo electrónico para restablecer o contrasinal", @@ -36,7 +55,6 @@ "Finish setup" => "Rematar configuración", "web services under your control" => "servizos web baixo o seu control", "Log out" => "Desconectar", -"Settings" => "Preferencias", "Lost your password?" => "Perdeu o contrasinal?", "remember" => "lembrar", "Log in" => "Conectar", diff --git a/core/l10n/he.php b/core/l10n/he.php index 1929681e06..f0a18103a8 100644 --- a/core/l10n/he.php +++ b/core/l10n/he.php @@ -2,7 +2,25 @@ "Application name not provided." => "שם היישום לא סופק.", "No category to add?" => "אין קטגוריה להוספה?", "This category already exists: " => "קטגוריה זאת כבר קיימת: ", -"Owncloud password reset" => "איפוס הססמה של ownCloud", +"Settings" => "הגדרות", +"January" => "ינואר", +"February" => "פברואר", +"March" => "מרץ", +"April" => "אפריל", +"May" => "מאי", +"June" => "יוני", +"July" => "יולי", +"August" => "אוגוסט", +"September" => "ספטמבר", +"October" => "אוקטובר", +"November" => "נובמבר", +"December" => "דצמבר", +"Cancel" => "ביטול", +"No" => "לא", +"Yes" => "כן", +"Ok" => "בסדר", +"No categories selected for deletion." => "לא נבחרו קטגוריות למחיקה", +"Error" => "שגיאה", "ownCloud password reset" => "איפוס הססמה של ownCloud", "Use the following link to reset your password: {link}" => "יש להשתמש בקישור הבא כדי לאפס את הססמה שלך: {link}", "You will receive a link to reset your password via Email." => "יישלח לתיבת הדוא״ל שלך קישור לאיפוס הססמה.", @@ -36,7 +54,6 @@ "Finish setup" => "סיום התקנה", "web services under your control" => "שירותי רשת בשליטתך", "Log out" => "התנתקות", -"Settings" => "הגדרות", "Lost your password?" => "שכחת את ססמתך?", "remember" => "שמירת הססמה", "Log in" => "כניסה", diff --git a/core/l10n/hr.php b/core/l10n/hr.php index 92be804804..d4e773afc1 100644 --- a/core/l10n/hr.php +++ b/core/l10n/hr.php @@ -2,7 +2,26 @@ "Application name not provided." => "Ime aplikacije nije pribavljeno.", "No category to add?" => "Nemate kategorija koje možete dodati?", "This category already exists: " => "Ova kategorija već postoji: ", -"Owncloud password reset" => "ownCloud resetiranje lozinke", +"ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=" => "ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=", +"Settings" => "Postavke", +"January" => "Siječanj", +"February" => "Veljača", +"March" => "Ožujak", +"April" => "Travanj", +"May" => "Svibanj", +"June" => "Lipanj", +"July" => "Srpanj", +"August" => "Kolovoz", +"September" => "Rujan", +"October" => "Listopad", +"November" => "Studeni", +"December" => "Prosinac", +"Cancel" => "Odustani", +"No" => "Ne", +"Yes" => "Da", +"Ok" => "U redu", +"No categories selected for deletion." => "Nema odabranih kategorija za brisanje.", +"Error" => "Pogreška", "ownCloud password reset" => "ownCloud resetiranje lozinke", "Use the following link to reset your password: {link}" => "Koristite ovaj link da biste poništili lozinku: {link}", "You will receive a link to reset your password via Email." => "Primit ćete link kako biste poništili zaporku putem e-maila.", @@ -36,7 +55,6 @@ "Finish setup" => "Završi postavljanje", "web services under your control" => "web usluge pod vašom kontrolom", "Log out" => "Odjava", -"Settings" => "Postavke", "Lost your password?" => "Izgubili ste lozinku?", "remember" => "zapamtiti", "Log in" => "Prijava", diff --git a/core/l10n/hu_HU.php b/core/l10n/hu_HU.php index fc1337d8de..dbfe2781d5 100644 --- a/core/l10n/hu_HU.php +++ b/core/l10n/hu_HU.php @@ -2,45 +2,63 @@ "Application name not provided." => "Alkalmazásnév hiányzik", "No category to add?" => "Nincs hozzáadandó kategória?", "This category already exists: " => "Ez a kategória már létezik", -"Owncloud password reset" => "ownCloud jelszó-visszaállítás", +"ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=" => "ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=", +"Settings" => "Beállítások", +"January" => "Január", +"February" => "Február", +"March" => "Március", +"April" => "Április", +"May" => "Május", +"June" => "Június", +"July" => "Július", +"August" => "Augusztus", +"September" => "Szeptember", +"October" => "Október", +"November" => "November", +"December" => "December", +"Cancel" => "Mégse", +"No" => "Nem", +"Yes" => "Igen", +"Ok" => "Ok", +"No categories selected for deletion." => "Nincs törlésre jelölt kategória", +"Error" => "Hiba", "ownCloud password reset" => "ownCloud jelszó-visszaállítás", "Use the following link to reset your password: {link}" => "Használja az alábbi linket a jelszó-visszaállításhoz: {link}", "You will receive a link to reset your password via Email." => "Egy e-mailben kap értesítést a jelszóváltoztatás módjáról.", -"Requested" => "Kért", +"Requested" => "Kérés elküldve", "Login failed!" => "Belépés sikertelen!", -"Username" => "Felhasználói név", +"Username" => "Felhasználónév", "Request reset" => "Visszaállítás igénylése", -"Your password was reset" => "Jelszó megváltoztatásra került", +"Your password was reset" => "Jelszó megváltoztatva", "To login page" => "A bejelentkező ablakhoz", "New password" => "Új jelszó", -"Reset password" => "Jelszó beállítás", +"Reset password" => "Jelszó-visszaállítás", "Personal" => "Személyes", "Users" => "Felhasználók", "Apps" => "Alkalmazások", -"Admin" => "Adminisztráció", +"Admin" => "Admin", "Help" => "Súgó", "Access forbidden" => "Hozzáférés tiltva", -"Cloud not found" => "Nem talált felhő", +"Cloud not found" => "A felhő nem található", "Edit categories" => "Kategóriák szerkesztése", "Add" => "Hozzáadás", -"Create an admin account" => "Adminisztrációs fiók létrehozása", +"Create an admin account" => "Rendszergazdafiók létrehozása", "Password" => "Jelszó", -"Advanced" => "Fejlett", -"Data folder" => "Adat könyvtár", +"Advanced" => "Haladó", +"Data folder" => "Adatkönyvtár", "Configure the database" => "Adatbázis konfigurálása", "will be used" => "használva lesz", -"Database user" => "Adatbázis felhasználó", +"Database user" => "Adatbázis felhasználónév", "Database password" => "Adatbázis jelszó", "Database name" => "Adatbázis név", "Database host" => "Adatbázis szerver", -"Finish setup" => "Beállítások befejezése", +"Finish setup" => "Beállítás befejezése", "web services under your control" => "webszolgáltatások az irányításod alatt", "Log out" => "Kilépés", -"Settings" => "Beállítások", "Lost your password?" => "Elfelejtett jelszó?", -"remember" => "emlékezni", +"remember" => "emlékezzen", "Log in" => "Bejelentkezés", -"You are logged out." => "Kilépés sikerült.", +"You are logged out." => "Kilépett.", "prev" => "Előző", "next" => "Következő" ); diff --git a/core/l10n/ia.php b/core/l10n/ia.php index 97e8dfc147..e202daafa3 100644 --- a/core/l10n/ia.php +++ b/core/l10n/ia.php @@ -1,6 +1,6 @@ "Iste categoria jam existe:", -"Owncloud password reset" => "Reinitialisation del contrasigno de Owncloud", +"Settings" => "Configurationes", "ownCloud password reset" => "Reinitialisation del contrasigno de ownCLoud", "Requested" => "Requestate", "Login failed!" => "Initio de session fallite!", @@ -31,7 +31,6 @@ "Database host" => "Hospite de base de datos", "web services under your control" => "servicios web sub tu controlo", "Log out" => "Clauder le session", -"Settings" => "Configurationes", "Lost your password?" => "Tu perdeva le contrasigno?", "remember" => "memora", "Log in" => "Aperir session", diff --git a/core/l10n/id.php b/core/l10n/id.php index f9fa7d2bb9..296e2d62a4 100644 --- a/core/l10n/id.php +++ b/core/l10n/id.php @@ -2,7 +2,7 @@ "Application name not provided." => "Nama aplikasi tidak diberikan.", "No category to add?" => "Tidak ada kategori yang akan ditambahkan?", "This category already exists: " => "Kategori ini sudah ada:", -"Owncloud password reset" => "Reset password Owncloud", +"Settings" => "Setelan", "ownCloud password reset" => "reset password ownCloud", "Use the following link to reset your password: {link}" => "Gunakan tautan berikut untuk mereset password anda: {link}", "You will receive a link to reset your password via Email." => "Anda akan mendapatkan link untuk mereset password anda lewat Email.", @@ -36,7 +36,6 @@ "Finish setup" => "Selesaikan instalasi", "web services under your control" => "web service dibawah kontrol anda", "Log out" => "Keluar", -"Settings" => "Setelan", "Lost your password?" => "Lupa password anda?", "remember" => "selalu login", "Log in" => "Masuk", diff --git a/core/l10n/it.php b/core/l10n/it.php index ce2352f033..1c0bf94ca7 100644 --- a/core/l10n/it.php +++ b/core/l10n/it.php @@ -2,7 +2,26 @@ "Application name not provided." => "Nome dell'applicazione non fornito.", "No category to add?" => "Nessuna categoria da aggiungere?", "This category already exists: " => "Questa categoria esiste già: ", -"Owncloud password reset" => "Ripristino password di Owncloud", +"ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=" => "ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=", +"Settings" => "Impostazioni", +"January" => "Gennaio", +"February" => "Febbraio", +"March" => "Marzo", +"April" => "Aprile", +"May" => "Maggio", +"June" => "Giugno", +"July" => "Luglio", +"August" => "Agosto", +"September" => "Settembre", +"October" => "Ottobre", +"November" => "Novembre", +"December" => "Dicembre", +"Cancel" => "Annulla", +"No" => "No", +"Yes" => "Sì", +"Ok" => "Ok", +"No categories selected for deletion." => "Nessuna categoria selezionata per l'eliminazione.", +"Error" => "Errore", "ownCloud password reset" => "Ripristino password di ownCloud", "Use the following link to reset your password: {link}" => "Usa il collegamento seguente per ripristinare la password: {link}", "You will receive a link to reset your password via Email." => "Riceverai un collegamento per ripristinare la tua password via email", @@ -36,7 +55,6 @@ "Finish setup" => "Termina la configurazione", "web services under your control" => "servizi web nelle tue mani", "Log out" => "Esci", -"Settings" => "Impostazioni", "Lost your password?" => "Hai perso la password?", "remember" => "ricorda", "Log in" => "Accedi", diff --git a/core/l10n/ja_JP.php b/core/l10n/ja_JP.php index 50b8e9e616..5f9b9da33a 100644 --- a/core/l10n/ja_JP.php +++ b/core/l10n/ja_JP.php @@ -2,7 +2,26 @@ "Application name not provided." => "アプリケーション名は提供されていません。", "No category to add?" => "追加するカテゴリはありませんか?", "This category already exists: " => "このカテゴリはすでに存在します: ", -"Owncloud password reset" => "Owncloud のパスワードをリセット", +"ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=" => "ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=", +"Settings" => "設定", +"January" => "1月", +"February" => "2月", +"March" => "3月", +"April" => "4月", +"May" => "5月", +"June" => "6月", +"July" => "7月", +"August" => "8月", +"September" => "9月", +"October" => "10月", +"November" => "11月", +"December" => "12月", +"Cancel" => "キャンセル", +"No" => "いいえ", +"Yes" => "はい", +"Ok" => "OK", +"No categories selected for deletion." => "削除するカテゴリが選択されていません。", +"Error" => "エラー", "ownCloud password reset" => "ownCloudのパスワードをリセットします", "Use the following link to reset your password: {link}" => "パスワードをリセットするには次のリンクをクリックして下さい: {link}", "You will receive a link to reset your password via Email." => "メールでパスワードをリセットするリンクが届きます。", @@ -36,7 +55,6 @@ "Finish setup" => "セットアップを完了します", "web services under your control" => "管理下にあるウェブサービス", "Log out" => "ログアウト", -"Settings" => "設定", "Lost your password?" => "パスワードを忘れましたか?", "remember" => "パスワードを記憶する", "Log in" => "ログイン", diff --git a/core/l10n/ko.php b/core/l10n/ko.php index 44396b94f8..5a330581ff 100644 --- a/core/l10n/ko.php +++ b/core/l10n/ko.php @@ -2,7 +2,26 @@ "Application name not provided." => "응용 프로그램의 이름이 규정되어 있지 않습니다. ", "No category to add?" => "추가할 카테고리가 없습니까?", "This category already exists: " => "이 카테고리는 이미 존재합니다:", -"Owncloud password reset" => "Owncloud 암호 재설정", +"ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=" => "ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=", +"Settings" => "설정", +"January" => "1월", +"February" => "2월", +"March" => "3월", +"April" => "4월", +"May" => "5월", +"June" => "6월", +"July" => "7월", +"August" => "8월", +"September" => "9월", +"October" => "10월", +"November" => "11월", +"December" => "12월", +"Cancel" => "취소", +"No" => "아니오", +"Yes" => "예", +"Ok" => "승락", +"No categories selected for deletion." => "삭제 카테고리를 선택하지 않았습니다.", +"Error" => "에러", "ownCloud password reset" => "ownCloud 비밀번호 재설정", "Use the following link to reset your password: {link}" => "다음 링크를 사용하여 암호를 초기화할 수 있습니다: {link}", "You will receive a link to reset your password via Email." => "전자 우편으로 암호 재설정 링크를 보냈습니다.", @@ -36,7 +55,6 @@ "Finish setup" => "설치 완료", "web services under your control" => "내가 관리하는 웹 서비스", "Log out" => "로그아웃", -"Settings" => "설정", "Lost your password?" => "암호를 잊으셨습니까?", "remember" => "기억하기", "Log in" => "로그인", diff --git a/core/l10n/lb.php b/core/l10n/lb.php index dd94201138..eafdcc3737 100644 --- a/core/l10n/lb.php +++ b/core/l10n/lb.php @@ -2,7 +2,7 @@ "Application name not provided." => "Numm vun der Applikatioun ass net uginn.", "No category to add?" => "Keng Kategorie fir bäizesetzen?", "This category already exists: " => "Des Kategorie existéiert schonn:", -"Owncloud password reset" => "Owncloud Passwuert reset", +"Settings" => "Astellungen", "ownCloud password reset" => "ownCloud Passwuert reset", "Use the following link to reset your password: {link}" => "Benotz folgende Link fir däi Passwuert ze reseten: {link}", "You will receive a link to reset your password via Email." => "Du kriss en Link fir däin Passwuert nei ze setzen via Email geschéckt.", @@ -36,7 +36,6 @@ "Finish setup" => "Installatioun ofschléissen", "web services under your control" => "Web Servicer ënnert denger Kontroll", "Log out" => "Ausloggen", -"Settings" => "Astellungen", "Lost your password?" => "Passwuert vergiess?", "remember" => "verhalen", "Log in" => "Log dech an", diff --git a/core/l10n/lt_LT.php b/core/l10n/lt_LT.php index 5dedfffabd..f36f697b67 100644 --- a/core/l10n/lt_LT.php +++ b/core/l10n/lt_LT.php @@ -1,6 +1,27 @@ "Nepateiktas programos pavadinimas.", +"No category to add?" => "Nepridėsite jokios kategorijos?", "This category already exists: " => "Tokia kategorija jau yra:", -"Owncloud password reset" => "Owncloud slaptažodžio atkūrimas", +"ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=" => "ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=", +"Settings" => "Nustatymai", +"January" => "Sausis", +"February" => "Vasaris", +"March" => "Kovas", +"April" => "Balandis", +"May" => "Gegužė", +"June" => "Birželis", +"July" => "Liepa", +"August" => "Rugpjūtis", +"September" => "Rugsėjis", +"October" => "Spalis", +"November" => "Lapkritis", +"December" => "Gruodis", +"Cancel" => "Atšaukti", +"No" => "Ne", +"Yes" => "Taip", +"Ok" => "Gerai", +"No categories selected for deletion." => "Trynimui nepasirinkta jokia kategorija.", +"Error" => "Klaida", "ownCloud password reset" => "ownCloud slaptažodžio atkūrimas", "Use the following link to reset your password: {link}" => "Slaptažodio atkūrimui naudokite šią nuorodą: {link}", "You will receive a link to reset your password via Email." => "Elektroniniu paštu gausite nuorodą, su kuria galėsite iš naujo nustatyti slaptažodį.", @@ -34,7 +55,6 @@ "Finish setup" => "Baigti diegimą", "web services under your control" => "jūsų valdomos web paslaugos", "Log out" => "Atsijungti", -"Settings" => "Nustatymai", "Lost your password?" => "Pamiršote slaptažodį?", "remember" => "prisiminti", "Log in" => "Prisijungti", diff --git a/core/l10n/lv.php b/core/l10n/lv.php new file mode 100644 index 0000000000..6435c50158 --- /dev/null +++ b/core/l10n/lv.php @@ -0,0 +1,35 @@ + "Iestatījumi", +"Use the following link to reset your password: {link}" => "Izmantojiet šo linku lai mainītu paroli", +"You will receive a link to reset your password via Email." => "Jūs savā epastā saņemsiet interneta saiti, caur kuru varēsiet atjaunot paroli.", +"Requested" => "Obligāts", +"Login failed!" => "Neizdevās ielogoties.", +"Username" => "Lietotājvārds", +"Request reset" => "Pieprasīt paroles maiņu", +"Your password was reset" => "Jūsu parole tika nomainīta", +"To login page" => "Uz ielogošanās lapu", +"New password" => "Jauna parole", +"Reset password" => "Mainīt paroli", +"Personal" => "Personīgi", +"Users" => "Lietotāji", +"Apps" => "Aplikācijas", +"Admin" => "Administrators", +"Help" => "Palīdzība", +"Cloud not found" => "Mākonis netika atrasts", +"Password" => "Parole", +"Data folder" => "Datu mape", +"Configure the database" => "Nokonfigurēt datubāzi", +"will be used" => "tiks izmantots", +"Database user" => "Datubāzes lietotājs", +"Database password" => "Datubāzes parole", +"Database name" => "Datubāzes nosaukums", +"Database host" => "Datubāzes mājvieta", +"Finish setup" => "Pabeigt uzstādījumus", +"Log out" => "Izlogoties", +"Lost your password?" => "Aizmirsāt paroli?", +"remember" => "atcerēties", +"Log in" => "Ielogoties", +"You are logged out." => "Jūs esat veiksmīgi izlogojies.", +"prev" => "iepriekšējā", +"next" => "nākamā" +); diff --git a/core/l10n/mk.php b/core/l10n/mk.php index 7baac36f39..af49a04f11 100644 --- a/core/l10n/mk.php +++ b/core/l10n/mk.php @@ -2,7 +2,26 @@ "Application name not provided." => "Име за апликацијата не е доставено.", "No category to add?" => "Нема категорија да се додаде?", "This category already exists: " => "Оваа категорија веќе постои:", -"Owncloud password reset" => "Ресетирање на Owncloud лозинка", +"ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=" => "ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=", +"Settings" => "Поставки", +"January" => "Јануари", +"February" => "Февруари", +"March" => "Март", +"April" => "Април", +"May" => "Мај", +"June" => "Јуни", +"July" => "Јули", +"August" => "Август", +"September" => "Септември", +"October" => "Октомври", +"November" => "Ноември", +"December" => "Декември", +"Cancel" => "Откажи", +"No" => "Не", +"Yes" => "Да", +"Ok" => "Во ред", +"No categories selected for deletion." => "Не е одбрана категорија за бришење.", +"Error" => "Грешка", "ownCloud password reset" => "ресетирање на лозинка за ownCloud", "Use the following link to reset your password: {link}" => "Користете ја следната врска да ја ресетирате Вашата лозинка: {link}", "You will receive a link to reset your password via Email." => "Ќе добиете врска по е-пошта за да може да ја ресетирате Вашата лозинка.", @@ -36,7 +55,6 @@ "Finish setup" => "Заврши го подесувањето", "web services under your control" => "веб сервиси под Ваша контрола", "Log out" => "Одјава", -"Settings" => "Поставки", "Lost your password?" => "Ја заборавивте лозинката?", "remember" => "запамти", "Log in" => "Најава", diff --git a/core/l10n/ms_MY.php b/core/l10n/ms_MY.php index 5cf7a04b41..25da7cd862 100644 --- a/core/l10n/ms_MY.php +++ b/core/l10n/ms_MY.php @@ -1,5 +1,28 @@ "Penetapan kata laluan Owncloud", +"Application name not provided." => "nama applikasi tidak disediakan", +"No category to add?" => "Tiada kategori untuk di tambah?", +"This category already exists: " => "Kategori ini telah wujud", +"ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=" => "ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=", +"Settings" => "Tetapan", +"January" => "Januari", +"February" => "Februari", +"March" => "Mac", +"April" => "April", +"May" => "Mei", +"June" => "Jun", +"July" => "Julai", +"August" => "Ogos", +"September" => "September", +"October" => "Oktober", +"November" => "November", +"December" => "Disember", +"Cancel" => "Batal", +"No" => "Tidak", +"Yes" => "Ya", +"Ok" => "Ok", +"No categories selected for deletion." => "tiada kategori dipilih untuk penghapusan", +"Error" => "Ralat", +"ownCloud password reset" => "Set semula kata lalaun ownCloud", "Use the following link to reset your password: {link}" => "Guna pautan berikut untuk menetapkan semula kata laluan anda: {link}", "You will receive a link to reset your password via Email." => "Anda akan menerima pautan untuk menetapkan semula kata laluan anda melalui emel", "Requested" => "Meminta", @@ -15,7 +38,9 @@ "Apps" => "Aplikasi", "Admin" => "Admin", "Help" => "Bantuan", +"Access forbidden" => "Larangan akses", "Cloud not found" => "Awan tidak dijumpai", +"Edit categories" => "Edit kategori", "Add" => "Tambah", "Create an admin account" => "buat akaun admin", "Password" => "Kata laluan", @@ -30,7 +55,6 @@ "Finish setup" => "Setup selesai", "web services under your control" => "Perkhidmatan web di bawah kawalan anda", "Log out" => "Log keluar", -"Settings" => "Tetapan", "Lost your password?" => "Hilang kata laluan?", "remember" => "ingat", "Log in" => "Log masuk", diff --git a/core/l10n/nb_NO.php b/core/l10n/nb_NO.php index 80b9da0e1e..a8bfebb8a5 100644 --- a/core/l10n/nb_NO.php +++ b/core/l10n/nb_NO.php @@ -2,7 +2,25 @@ "Application name not provided." => "Applikasjonsnavn ikke angitt.", "No category to add?" => "Ingen kategorier å legge til?", "This category already exists: " => "Denne kategorien finnes allerede:", -"Owncloud password reset" => "OwnCloud passordtilbakestilling", +"Settings" => "Innstillinger", +"January" => "Januar", +"February" => "Februar", +"March" => "Mars", +"April" => "April", +"May" => "Mai", +"June" => "Juni", +"July" => "Juli", +"August" => "August", +"September" => "September", +"October" => "Oktober", +"November" => "November", +"December" => "Desember", +"Cancel" => "Avbryt", +"No" => "Nei", +"Yes" => "Ja", +"Ok" => "Ok", +"No categories selected for deletion." => "Ingen kategorier merket for sletting.", +"Error" => "Feil", "ownCloud password reset" => "Tilbakestill ownCloud passord", "Use the following link to reset your password: {link}" => "Bruk følgende lenke for å tilbakestille passordet ditt: {link}", "You will receive a link to reset your password via Email." => "Du burde motta detaljer om å tilbakestille passordet ditt via epost.", @@ -36,7 +54,6 @@ "Finish setup" => "Fullfør oppsetting", "web services under your control" => "nettjenester under din kontroll", "Log out" => "Logg ut", -"Settings" => "Innstillinger", "Lost your password?" => "Mistet passordet ditt?", "remember" => "husk", "Log in" => "Logg inn", diff --git a/core/l10n/nl.php b/core/l10n/nl.php index 0b804f4d17..874a710a75 100644 --- a/core/l10n/nl.php +++ b/core/l10n/nl.php @@ -2,7 +2,25 @@ "Application name not provided." => "Applicatie naam niet gegeven.", "No category to add?" => "Geen categorie toevoegen?", "This category already exists: " => "De categorie bestaat al.", -"Owncloud password reset" => "Reset je ownCloud wachtwoord", +"Settings" => "Instellingen", +"January" => "Januari", +"February" => "Februari", +"March" => "Maart", +"April" => "April", +"May" => "Mei", +"June" => "Juni", +"July" => "Juli", +"August" => "Augustus", +"September" => "September", +"October" => "Oktober", +"November" => "November", +"December" => "December", +"Cancel" => "Annuleren", +"No" => "Nee", +"Yes" => "Ja", +"Ok" => "Ok", +"No categories selected for deletion." => "Geen categorie geselecteerd voor verwijdering.", +"Error" => "Fout", "ownCloud password reset" => "ownCloud wachtwoord herstellen", "Use the following link to reset your password: {link}" => "Gebruik de volgende link om je wachtwoord te resetten: {link}", "You will receive a link to reset your password via Email." => "U ontvangt een link om je wachtwoord opnieuw in te stellen via e-mail.", @@ -36,7 +54,6 @@ "Finish setup" => "Installatie afronden", "web services under your control" => "webdiensten die je beheerst", "Log out" => "Afmelden", -"Settings" => "Instellingen", "Lost your password?" => "Uw wachtwoord vergeten?", "remember" => "onthoud gegevens", "Log in" => "Meld je aan", diff --git a/core/l10n/nn_NO.php b/core/l10n/nn_NO.php index aeb8051738..9dfce36049 100644 --- a/core/l10n/nn_NO.php +++ b/core/l10n/nn_NO.php @@ -1,5 +1,5 @@ "Owncloud Passord tilbakestilling", +"Settings" => "Innstillingar", "Use the following link to reset your password: {link}" => "Bruk føljane link til å tilbakestille passordet ditt: {link}", "You will receive a link to reset your password via Email." => "Du vil få ei lenkje for å nullstilla passordet via epost.", "Requested" => "Førespurt", @@ -29,7 +29,6 @@ "Finish setup" => "Fullfør oppsettet", "web services under your control" => "Vev tjenester under din kontroll", "Log out" => "Logg ut", -"Settings" => "Innstillingar", "Lost your password?" => "Gløymt passordet?", "remember" => "hugs", "Log in" => "Logg inn", diff --git a/core/l10n/pl.php b/core/l10n/pl.php index 3c6f28c856..4eb5f1f9ae 100644 --- a/core/l10n/pl.php +++ b/core/l10n/pl.php @@ -2,7 +2,26 @@ "Application name not provided." => "Brak nazwy dla aplikacji", "No category to add?" => "Brak kategorii", "This category already exists: " => "Ta kategoria już istnieje", -"Owncloud password reset" => "Resetowanie hasła", +"ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=" => "ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=", +"Settings" => "Ustawienia", +"January" => "Styczeń", +"February" => "Luty", +"March" => "Marzec", +"April" => "Kwiecień", +"May" => "Maj", +"June" => "Czerwiec", +"July" => "Lipiec", +"August" => "Sierpień", +"September" => "Wrzesień", +"October" => "Październik", +"November" => "Listopad", +"December" => "Grudzień", +"Cancel" => "Anuluj", +"No" => "Nie", +"Yes" => "Tak", +"Ok" => "Ok", +"No categories selected for deletion." => "Nie ma kategorii zaznaczonych do usunięcia.", +"Error" => "Błąd", "ownCloud password reset" => "restart hasła", "Use the following link to reset your password: {link}" => "Proszę użyć tego odnośnika do zresetowania hasła: {link}", "You will receive a link to reset your password via Email." => "Odnośnik służący do resetowania hasła zostanie wysłany na adres e-mail.", @@ -36,7 +55,6 @@ "Finish setup" => "Zakończ konfigurowanie", "web services under your control" => "usługi internetowe pod kontrolą", "Log out" => "Wylogowuje użytkownika", -"Settings" => "Ustawienia", "Lost your password?" => "Nie pamiętasz hasła?", "remember" => "Zapamiętanie", "Log in" => "Zaloguj", diff --git a/core/l10n/pt_BR.php b/core/l10n/pt_BR.php index 6f01d666f7..46d601e6eb 100644 --- a/core/l10n/pt_BR.php +++ b/core/l10n/pt_BR.php @@ -2,7 +2,26 @@ "Application name not provided." => "Nome da aplicação não foi fornecido.", "No category to add?" => "Nenhuma categoria adicionada?", "This category already exists: " => "Essa categoria já existe", -"Owncloud password reset" => "Mudar senha do Owncloud", +"ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=" => "ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=", +"Settings" => "Configurações", +"January" => "Janeiro", +"February" => "Fevereiro", +"March" => "Março", +"April" => "Abril", +"May" => "Maio", +"June" => "Junho", +"July" => "Julho", +"August" => "Agosto", +"September" => "Setembro", +"October" => "Outubro", +"November" => "Novembro", +"December" => "Dezembro", +"Cancel" => "Cancelar", +"No" => "Não", +"Yes" => "Sim", +"Ok" => "Ok", +"No categories selected for deletion." => "Nenhuma categoria selecionada para deletar.", +"Error" => "Erro", "ownCloud password reset" => "Redefinir senha ownCloud", "Use the following link to reset your password: {link}" => "Use o seguinte link para redefinir sua senha: {link}", "You will receive a link to reset your password via Email." => "Você receberá um link para redefinir sua senha via e-mail.", @@ -36,7 +55,6 @@ "Finish setup" => "Concluir configuração", "web services under your control" => "web services sob seu controle", "Log out" => "Sair", -"Settings" => "Configurações", "Lost your password?" => "Esqueçeu sua senha?", "remember" => "lembrete", "Log in" => "Log in", diff --git a/core/l10n/pt_PT.php b/core/l10n/pt_PT.php index 55c4c96e5c..29135f0d66 100644 --- a/core/l10n/pt_PT.php +++ b/core/l10n/pt_PT.php @@ -2,7 +2,26 @@ "Application name not provided." => "Nome da aplicação não definida.", "No category to add?" => "Nenhuma categoria para adicionar?", "This category already exists: " => "Esta categoria já existe:", -"Owncloud password reset" => "Redefinir palavra-chave ownCloud", +"ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=" => "ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=", +"Settings" => "Definições", +"January" => "Janeiro", +"February" => "Fevereiro", +"March" => "Março", +"April" => "Abril", +"May" => "Maio", +"June" => "Junho", +"July" => "Julho", +"August" => "Agosto", +"September" => "Setembro", +"October" => "Outubro", +"November" => "Novembro", +"December" => "Dezembro", +"Cancel" => "Cancelar", +"No" => "Não", +"Yes" => "Sim", +"Ok" => "Ok", +"No categories selected for deletion." => "Nenhuma categoria seleccionar para eliminar", +"Error" => "Erro", "ownCloud password reset" => "Reposição da password ownCloud", "Use the following link to reset your password: {link}" => "Use o seguinte endereço para repor a sua password: {link}", "You will receive a link to reset your password via Email." => "Vai receber um endereço para repor a sua password", @@ -36,7 +55,6 @@ "Finish setup" => "Acabar instalação", "web services under your control" => "serviços web sob o seu controlo", "Log out" => "Sair", -"Settings" => "Definições", "Lost your password?" => "Esqueceu a sua password?", "remember" => "lembrar", "Log in" => "Entrar", diff --git a/core/l10n/ro.php b/core/l10n/ro.php index b6170d8e96..484a47727d 100644 --- a/core/l10n/ro.php +++ b/core/l10n/ro.php @@ -2,7 +2,7 @@ "Application name not provided." => "Numele aplicație nu este furnizat.", "No category to add?" => "Nici o categorie de adăugat?", "This category already exists: " => "Această categorie deja există:", -"Owncloud password reset" => "Resetarea parolei ownCloud", +"Settings" => "Configurări", "ownCloud password reset" => "Resetarea parolei ownCloud ", "Use the following link to reset your password: {link}" => "Folosește următorul link pentru a reseta parola: {link}", "You will receive a link to reset your password via Email." => "Vei primi un mesaj prin care vei putea reseta parola via email", @@ -36,7 +36,6 @@ "Finish setup" => "Finalizează instalarea", "web services under your control" => "servicii web controlate de tine", "Log out" => "Ieșire", -"Settings" => "Configurări", "Lost your password?" => "Ai uitat parola?", "remember" => "amintește", "Log in" => "Autentificare", diff --git a/core/l10n/ru.php b/core/l10n/ru.php index ff86f29b26..edb5557777 100644 --- a/core/l10n/ru.php +++ b/core/l10n/ru.php @@ -2,7 +2,26 @@ "Application name not provided." => "Имя приложения не установлено.", "No category to add?" => "Нет категорий для добавления?", "This category already exists: " => "Эта категория уже существует: ", -"Owncloud password reset" => "Сброс пароля OwnCloud", +"ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=" => "ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=", +"Settings" => "Настройки", +"January" => "Январь", +"February" => "Февраль", +"March" => "Март", +"April" => "Апрель", +"May" => "Май", +"June" => "Июнь", +"July" => "Июль", +"August" => "Август", +"September" => "Сентябрь", +"October" => "Октябрь", +"November" => "Ноябрь", +"December" => "Декабрь", +"Cancel" => "Отмена", +"No" => "Нет", +"Yes" => "Да", +"Ok" => "Ок", +"No categories selected for deletion." => "Нет категорий для удаления.", +"Error" => "Ошибка", "ownCloud password reset" => "Сброс пароля ", "Use the following link to reset your password: {link}" => "Используйте следующую ссылку чтобы сбросить пароль: {link}", "You will receive a link to reset your password via Email." => "На ваш адрес Email выслана ссылка для сброса пароля.", @@ -36,7 +55,6 @@ "Finish setup" => "Завершить установку", "web services under your control" => "Сетевые службы под твоим контролем", "Log out" => "Выйти", -"Settings" => "Настройки", "Lost your password?" => "Забыли пароль?", "remember" => "запомнить", "Log in" => "Войти", diff --git a/core/l10n/sk_SK.php b/core/l10n/sk_SK.php index b09b4f5611..b6bff1e049 100644 --- a/core/l10n/sk_SK.php +++ b/core/l10n/sk_SK.php @@ -2,7 +2,26 @@ "Application name not provided." => "Meno aplikácie nezadané.", "No category to add?" => "Žiadna kategória pre pridanie?", "This category already exists: " => "Táto kategória už existuje:", -"Owncloud password reset" => "Obnova Owncloud hesla", +"ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=" => "ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=", +"Settings" => "Nastavenia", +"January" => "Január", +"February" => "Február", +"March" => "Marec", +"April" => "Apríl", +"May" => "Máj", +"June" => "Jún", +"July" => "Júl", +"August" => "August", +"September" => "September", +"October" => "Október", +"November" => "November", +"December" => "December", +"Cancel" => "Zrušiť", +"No" => "Nie", +"Yes" => "Áno", +"Ok" => "Ok", +"No categories selected for deletion." => "Neboli vybrané žiadne kategórie pre odstránenie.", +"Error" => "Chyba", "ownCloud password reset" => "Obnovenie hesla pre ownCloud", "Use the following link to reset your password: {link}" => "Použite nasledujúci odkaz pre obnovenie vášho hesla: {link}", "You will receive a link to reset your password via Email." => "Odkaz pre obnovenie hesla obdržíte E-mailom.", @@ -36,7 +55,6 @@ "Finish setup" => "Dokončiť inštaláciu", "web services under your control" => "webové služby pod vašou kontrolou", "Log out" => "Odhlásiť", -"Settings" => "Nastavenia", "Lost your password?" => "Zabudli ste heslo?", "remember" => "zapamätať", "Log in" => "Prihlásiť sa", diff --git a/core/l10n/sl.php b/core/l10n/sl.php index 5cd8498ea2..2f998c9549 100644 --- a/core/l10n/sl.php +++ b/core/l10n/sl.php @@ -2,7 +2,26 @@ "Application name not provided." => "Ime aplikacije ni bilo določeno.", "No category to add?" => "Ni kategorije za dodajanje?", "This category already exists: " => "Ta kategorija že obstaja:", -"Owncloud password reset" => "Ponastavi ownCloud geslo", +"ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=" => "ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=", +"Settings" => "Nastavitve", +"January" => "januar", +"February" => "februar", +"March" => "marec", +"April" => "april", +"May" => "maj", +"June" => "junij", +"July" => "julij", +"August" => "avgust", +"September" => "september", +"October" => "oktober", +"November" => "november", +"December" => "december", +"Cancel" => "Prekliči", +"No" => "Ne", +"Yes" => "Da", +"Ok" => "V redu", +"No categories selected for deletion." => "Za izbris ni bila izbrana nobena kategorija.", +"Error" => "Napaka", "ownCloud password reset" => "Ponastavitev gesla ownCloud", "Use the following link to reset your password: {link}" => "Uporabite sledečo povezavo za ponastavitev gesla: {link}", "You will receive a link to reset your password via Email." => "Na e-pošto boste prejeli povezavo s katero lahko ponastavite vaše geslo.", @@ -36,7 +55,6 @@ "Finish setup" => "Dokončaj namestitev", "web services under your control" => "spletne storitve pod vašim nadzorom", "Log out" => "Odjava", -"Settings" => "Nastavitve", "Lost your password?" => "Ste pozabili vaše geslo?", "remember" => "Zapomni si me", "Log in" => "Prijava", diff --git a/core/l10n/sr.php b/core/l10n/sr.php index 6bd6275df4..c2f2f07640 100644 --- a/core/l10n/sr.php +++ b/core/l10n/sr.php @@ -1,5 +1,5 @@ "Ресетовање лозинке за Оунклауд", +"Settings" => "Подешавања", "Use the following link to reset your password: {link}" => "Овом везом ресетујте своју лозинку: {link}", "You will receive a link to reset your password via Email." => "Добићете везу за ресетовање лозинке путем е-поште.", "Requested" => "Захтевано", @@ -29,7 +29,6 @@ "Finish setup" => "Заврши подешавање", "web services under your control" => "веб сервиси под контролом", "Log out" => "Одјава", -"Settings" => "Подешавања", "Lost your password?" => "Изгубили сте лозинку?", "remember" => "упамти", "Log in" => "Пријава", diff --git a/core/l10n/sr@latin.php b/core/l10n/sr@latin.php index e240de011e..8bc20cf1a6 100644 --- a/core/l10n/sr@latin.php +++ b/core/l10n/sr@latin.php @@ -1,4 +1,5 @@ "Podešavanja", "You will receive a link to reset your password via Email." => "Dobićete vezu za resetovanje lozinke putem e-pošte.", "Requested" => "Zahtevano", "Login failed!" => "Nesupela prijava!", @@ -25,7 +26,6 @@ "Database host" => "Domaćin baze", "Finish setup" => "Završi podešavanje", "Log out" => "Odjava", -"Settings" => "Podešavanja", "Lost your password?" => "Izgubili ste lozinku?", "remember" => "upamti", "You are logged out." => "Odjavljeni ste.", diff --git a/core/l10n/sv.php b/core/l10n/sv.php index fd53952184..25ba95558e 100644 --- a/core/l10n/sv.php +++ b/core/l10n/sv.php @@ -1,20 +1,39 @@ "Programnamn har inte angetts", +"Application name not provided." => "Programnamn har inte angetts.", "No category to add?" => "Ingen kategori att lägga till?", "This category already exists: " => "Denna kategori finns redan:", -"Owncloud password reset" => "Owncloud lösenordsåterställning", +"ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=" => "ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=", +"Settings" => "Inställningar", +"January" => "Januari", +"February" => "Februari", +"March" => "Mars", +"April" => "April", +"May" => "Maj", +"June" => "Juni", +"July" => "Juli", +"August" => "Augusti", +"September" => "September", +"October" => "Oktober", +"November" => "November", +"December" => "December", +"Cancel" => "Avbryt", +"No" => "Nej", +"Yes" => "Ja", +"Ok" => "Ok", +"No categories selected for deletion." => "Inga kategorier valda för radering.", +"Error" => "Fel", "ownCloud password reset" => "ownCloud lösenordsåterställning", "Use the following link to reset your password: {link}" => "Använd följande länk för att återställa lösenordet: {link}", "You will receive a link to reset your password via Email." => "Du får en länk att återställa ditt lösenord via e-post.", "Requested" => "Begärd", -"Login failed!" => "Inloggning misslyckades!", +"Login failed!" => "Misslyckad inloggning!", "Username" => "Användarnamn", "Request reset" => "Begär återställning", "Your password was reset" => "Ditt lösenord har återställts", -"To login page" => "Till logga in sidan", +"To login page" => "Till logginsidan", "New password" => "Nytt lösenord", "Reset password" => "Återställ lösenordet", -"Personal" => "Personlig", +"Personal" => "Personligt", "Users" => "Användare", "Apps" => "Program", "Admin" => "Admin", @@ -29,18 +48,17 @@ "Data folder" => "Datamapp", "Configure the database" => "Konfigurera databasen", "will be used" => "kommer att användas", -"Database user" => "Databas-användare", -"Database password" => "Lösenord för databasen", -"Database name" => "Databasens namn", +"Database user" => "Databasanvändare", +"Database password" => "Lösenord till databasen", +"Database name" => "Databasnamn", "Database host" => "Databasserver", "Finish setup" => "Avsluta installation", "web services under your control" => "webbtjänster under din kontroll", "Log out" => "Logga ut", -"Settings" => "Inställningar", "Lost your password?" => "Glömt ditt lösenord?", "remember" => "kom ihåg", "Log in" => "Logga in", -"You are logged out." => "Du är utloggad", +"You are logged out." => "Du är utloggad.", "prev" => "föregående", "next" => "nästa" ); diff --git a/core/l10n/th_TH.php b/core/l10n/th_TH.php index 2828381856..8bd4d36524 100644 --- a/core/l10n/th_TH.php +++ b/core/l10n/th_TH.php @@ -2,7 +2,26 @@ "Application name not provided." => "ยังไม่ได้ตั้งชื่อแอพพลิเคชั่น", "No category to add?" => "ไม่มีหมวดหมู่ที่ต้องการเพิ่ม?", "This category already exists: " => "หมวดหมู่นี้มีอยู่แล้ว: ", -"Owncloud password reset" => "เปลี่ยนรหัสผ่านใน Owncloud", +"ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=" => "ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=", +"Settings" => "ตั้งค่า", +"January" => "มกราคม", +"February" => "กุมภาพันธ์", +"March" => "มีนาคม", +"April" => "เมษายน", +"May" => "พฤษภาคม", +"June" => "มิถุนายน", +"July" => "กรกฏาคม", +"August" => "สิงหาคม", +"September" => "กันยายน", +"October" => "ตุลาคม", +"November" => "พฤศจิกายน", +"December" => "ธันวาคม", +"Cancel" => "ยกเลิก", +"No" => "ไม่ตกลง", +"Yes" => "ตกลง", +"Ok" => "ตกลง", +"No categories selected for deletion." => "ยังไม่ได้เลือกหมวดหมู่ที่ต้องการลบ", +"Error" => "พบข้อผิดพลาด", "ownCloud password reset" => "รีเซ็ตรหัสผ่าน ownCloud", "Use the following link to reset your password: {link}" => "ใช้ลิงค์ต่อไปนี้เพื่อเปลี่ยนรหัสผ่านของคุณใหม่: {link}", "You will receive a link to reset your password via Email." => "คุณจะได้รับลิงค์เพื่อกำหนดรหัสผ่านใหม่ทางอีเมล์", @@ -36,7 +55,6 @@ "Finish setup" => "ติดตั้งเรียบร้อยแล้ว", "web services under your control" => "web services under your control", "Log out" => "ออกจากระบบ", -"Settings" => "ตั้งค่า", "Lost your password?" => "ลืมรหัสผ่าน?", "remember" => "จำรหัสผ่าน", "Log in" => "เข้าสู่ระบบ", diff --git a/core/l10n/tr.php b/core/l10n/tr.php index fc0f791d59..e2d0d9b073 100644 --- a/core/l10n/tr.php +++ b/core/l10n/tr.php @@ -2,7 +2,26 @@ "Application name not provided." => "Uygulama adı verilmedi.", "No category to add?" => "Eklenecek kategori yok?", "This category already exists: " => "Bu kategori zaten mevcut: ", -"Owncloud password reset" => "Owncloud parola sıfırlama", +"ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=" => "ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=", +"Settings" => "Ayarlar", +"January" => "Ocak", +"February" => "Şubat", +"March" => "Mart", +"April" => "Nisan", +"May" => "Mayıs", +"June" => "Haziran", +"July" => "Temmuz", +"August" => "Ağustos", +"September" => "Eylül", +"October" => "Ekim", +"November" => "Kasım", +"December" => "Aralık", +"Cancel" => "İptal", +"No" => "Hayır", +"Yes" => "Evet", +"Ok" => "Tamam", +"No categories selected for deletion." => "Silmek için bir kategori seçilmedi", +"Error" => "Hata", "ownCloud password reset" => "ownCloud parola sıfırlama", "Use the following link to reset your password: {link}" => "Bu bağlantıyı kullanarak parolanızı sıfırlayın: {link}", "You will receive a link to reset your password via Email." => "Parolanızı sıfırlamak için bir bağlantı Eposta olarak gönderilecek.", @@ -36,7 +55,6 @@ "Finish setup" => "Kurulumu tamamla", "web services under your control" => "kontrolünüzdeki web servisleri", "Log out" => "Çıkış yap", -"Settings" => "Ayarlar", "Lost your password?" => "Parolanızı mı unuttunuz?", "remember" => "hatırla", "Log in" => "Giriş yap", diff --git a/core/l10n/uk.php b/core/l10n/uk.php index 091a8c9329..4a10a9fc74 100644 --- a/core/l10n/uk.php +++ b/core/l10n/uk.php @@ -1,4 +1,5 @@ "Налаштування", "You will receive a link to reset your password via Email." => "Ви отримаєте посилання для скидання вашого паролю на e-mail.", "Username" => "Ім'я користувача", "Your password was reset" => "Ваш пароль був скинутий", @@ -18,7 +19,6 @@ "Finish setup" => "Завершити налаштування", "web services under your control" => "веб-сервіс під вашим контролем", "Log out" => "Вихід", -"Settings" => "Налаштування", "Lost your password?" => "Забули пароль?", "remember" => "запам'ятати", "Log in" => "Вхід" diff --git a/core/l10n/vi.php b/core/l10n/vi.php new file mode 100644 index 0000000000..4a4c97032f --- /dev/null +++ b/core/l10n/vi.php @@ -0,0 +1,64 @@ + "Tên ứng dụng không tồn tại", +"No category to add?" => "Không có danh mục được thêm?", +"This category already exists: " => "Danh mục này đã được tạo :", +"ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=" => "ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=", +"Settings" => "Cài đặt", +"January" => "Tháng 1", +"February" => "Tháng 2", +"March" => "Tháng 3", +"April" => "Tháng 4", +"May" => "Tháng 5", +"June" => "Tháng 6", +"July" => "Tháng 7", +"August" => "Tháng 8", +"September" => "Tháng 9", +"October" => "Tháng 10", +"November" => "Tháng 11", +"December" => "Tháng 12", +"Cancel" => "Hủy", +"No" => "No", +"Yes" => "Yes", +"Ok" => "Ok", +"No categories selected for deletion." => "Không có thể loại nào được chọn để xóa.", +"Error" => "Lỗi", +"ownCloud password reset" => "Khôi phục mật khẩu Owncloud ", +"Use the following link to reset your password: {link}" => "Dùng đường dẫn sau để khôi phục lại mật khẩu : {link}", +"You will receive a link to reset your password via Email." => "Vui lòng kiểm tra Email để khôi phục lại mật khẩu.", +"Requested" => "Yêu cầu", +"Login failed!" => "Bạn đã nhập sai mật khẩu hay tên người dùng !", +"Username" => "Tên người dùng", +"Request reset" => "Yêu cầu thiết lập lại ", +"Your password was reset" => "Mật khẩu của bạn đã được khôi phục", +"To login page" => "Trang đăng nhập", +"New password" => "Mật khẩu mới", +"Reset password" => "Khôi phục mật khẩu", +"Personal" => "Cá nhân", +"Users" => "Người sử dụng", +"Apps" => "Ứng dụng", +"Admin" => "Quản trị", +"Help" => "Giúp đỡ", +"Access forbidden" => "Truy cập bị cấm ", +"Cloud not found" => "Không tìm thấy Clound", +"Edit categories" => "Sửa thể loại", +"Add" => "Thêm", +"Create an admin account" => "Tạo một tài khoản quản trị", +"Password" => "Mật khẩu", +"Advanced" => "Nâng cao", +"Data folder" => "Thư mục dữ liệu", +"Configure the database" => "Cấu hình Cơ Sở Dữ Liệu", +"will be used" => "được sử dụng", +"Database user" => "Người dùng cơ sở dữ liệu", +"Database password" => "Mật khẩu cơ sở dữ liệu", +"Database name" => "Tên cơ sở dữ liệu", +"Database host" => "Database host", +"Finish setup" => "Cài đặt hoàn tất", +"web services under your control" => "các dịch vụ web dưới sự kiểm soát của bạn", +"Log out" => "Đăng xuất", +"Lost your password?" => "Bạn quên mật khẩu ?", +"remember" => "Nhớ", +"Log in" => "Đăng nhập", +"You are logged out." => "Bạn đã đăng xuất.", +"prev" => "Lùi lại", +"next" => "Kế tiếp" +); diff --git a/core/l10n/zh_CN.GB2312.php b/core/l10n/zh_CN.GB2312.php new file mode 100644 index 0000000000..770d2b6772 --- /dev/null +++ b/core/l10n/zh_CN.GB2312.php @@ -0,0 +1,64 @@ + "应用程序并没有被提供.", +"No category to add?" => "没有分类添加了?", +"This category already exists: " => "这个分类已经存在了:", +"ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=" => "ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=", +"Settings" => "设置", +"January" => "一月", +"February" => "二月", +"March" => "三月", +"April" => "四月", +"May" => "五月", +"June" => "六月", +"July" => "七月", +"August" => "八月", +"September" => "九月", +"October" => "十月", +"November" => "十一月", +"December" => "十二月", +"Cancel" => "取消", +"No" => "否", +"Yes" => "是", +"Ok" => "好的", +"No categories selected for deletion." => "没有选者要删除的分类.", +"Error" => "错误", +"ownCloud password reset" => "私有云密码重置", +"Use the following link to reset your password: {link}" => "使用下面的链接来重置你的密码:{link}", +"You will receive a link to reset your password via Email." => "你将会收到一个重置密码的链接", +"Requested" => "请求", +"Login failed!" => "登陆失败!", +"Username" => "用户名", +"Request reset" => "要求重置", +"Your password was reset" => "你的密码已经被重置了", +"To login page" => "转至登陆页面", +"New password" => "新密码", +"Reset password" => "重置密码", +"Personal" => "个人的", +"Users" => "用户", +"Apps" => "应用程序", +"Admin" => "管理", +"Help" => "帮助", +"Access forbidden" => "禁止访问", +"Cloud not found" => "云 没有被找到", +"Edit categories" => "编辑分类", +"Add" => "添加", +"Create an admin account" => "建立一个 管理帐户", +"Password" => "密码", +"Advanced" => "进阶", +"Data folder" => "数据存放文件夹", +"Configure the database" => "配置数据库", +"will be used" => "将会使用", +"Database user" => "数据库用户", +"Database password" => "数据库密码", +"Database name" => "数据库用户名", +"Database host" => "数据库主机", +"Finish setup" => "完成安装", +"web services under your control" => "你控制下的网络服务", +"Log out" => "注销", +"Lost your password?" => "忘记密码?", +"remember" => "备忘", +"Log in" => "登陆", +"You are logged out." => "你已经注销了", +"prev" => "后退", +"next" => "前进" +); diff --git a/core/l10n/zh_CN.php b/core/l10n/zh_CN.php index e07add7cbb..1f5216a2ff 100644 --- a/core/l10n/zh_CN.php +++ b/core/l10n/zh_CN.php @@ -2,7 +2,25 @@ "Application name not provided." => "没有提供应用程序名称。", "No category to add?" => "没有可添加分类?", "This category already exists: " => "此分类已存在: ", -"Owncloud password reset" => "重置 Owncloud 密码", +"Settings" => "设置", +"January" => "一月", +"February" => "二月", +"March" => "三月", +"April" => "四月", +"May" => "五月", +"June" => "六月", +"July" => "七月", +"August" => "八月", +"September" => "九月", +"October" => "十月", +"November" => "十一月", +"December" => "十二月", +"Cancel" => "取消", +"No" => "否", +"Yes" => "是", +"Ok" => "好", +"No categories selected for deletion." => "没有选择要删除的类别", +"Error" => "错误", "ownCloud password reset" => "重置 ownCloud 密码", "Use the following link to reset your password: {link}" => "使用以下链接重置您的密码:{link}", "You will receive a link to reset your password via Email." => "您将会收到包含可以重置密码链接的邮件。", @@ -36,7 +54,6 @@ "Finish setup" => "安装完成", "web services under your control" => "由您掌控的网络服务", "Log out" => "注销", -"Settings" => "设置", "Lost your password?" => "忘记密码?", "remember" => "记住", "Log in" => "登录", diff --git a/core/l10n/zh_TW.php b/core/l10n/zh_TW.php index d958c62872..37c0d8cc9d 100644 --- a/core/l10n/zh_TW.php +++ b/core/l10n/zh_TW.php @@ -2,7 +2,7 @@ "Application name not provided." => "未提供應用程式名稱", "No category to add?" => "無分類添加?", "This category already exists: " => "此分類已經存在:", -"Owncloud password reset" => "私有雲重設密碼", +"Settings" => "設定", "ownCloud password reset" => "ownCloud 密碼重設", "Use the following link to reset your password: {link}" => "請循以下聯結重設你的密碼: (聯結) ", "You will receive a link to reset your password via Email." => "重設密碼的連結將會寄到你的電子郵件信箱", @@ -36,7 +36,6 @@ "Finish setup" => "完成設定", "web services under your control" => "網路服務已在你控制", "Log out" => "登出", -"Settings" => "設定", "Lost your password?" => "忘記密碼?", "remember" => "記住", "Log in" => "登入", diff --git a/core/lostpassword/index.php b/core/lostpassword/index.php index b32b56fcb3..8f86fe23aa 100644 --- a/core/lostpassword/index.php +++ b/core/lostpassword/index.php @@ -1,6 +1,6 @@ assign('link', $link); + $tmpl->assign('link', $link, false); $msg = $tmpl->fetchPage(); $l = OC_L10N::get('core'); - $from = 'lostpassword-noreply@' . OC_Helper::serverHost(); + $from = 'lostpassword-noreply@' . OCP\Util::getServerHost(); OC_MAIL::send($email,$_POST['user'],$l->t('ownCloud password reset'),$msg,$from,'ownCloud'); echo('sent'); diff --git a/core/lostpassword/resetpassword.php b/core/lostpassword/resetpassword.php index 1c78d72094..33be9d7053 100644 --- a/core/lostpassword/resetpassword.php +++ b/core/lostpassword/resetpassword.php @@ -1,6 +1,6 @@ output($files, $service); +} +else if ($service == 'core.js'){ + $minimizer = new OC_Minimizer_JS(); + $files = OC_TemplateLayout::findJavascriptFiles(OC_Util::$core_scripts); + $minimizer->output($files, $service); +} diff --git a/core/strings.php b/core/strings.php index 8c3f64ef14..01ab386608 100644 --- a/core/strings.php +++ b/core/strings.php @@ -7,4 +7,3 @@ $l->t("Users"); $l->t("Apps"); $l->t("Admin"); $l->t("Help"); -?> diff --git a/core/templates/404.php b/core/templates/404.php index cd4f2b40bb..13a8101034 100644 --- a/core/templates/404.php +++ b/core/templates/404.php @@ -10,6 +10,6 @@ if(!isset($_)){//also provide standalone error page
        • t( 'Cloud not found' ); ?>
          -

          +

        diff --git a/core/templates/exception.php b/core/templates/exception.php new file mode 100644 index 0000000000..7f58ce252c --- /dev/null +++ b/core/templates/exception.php @@ -0,0 +1,30 @@ +
          +
        • +
          + We're sorry, but something went terribly wrong.
          +

          + Bugtracker, please copy the following informations into the description.

          '; + }else{ + echo 'Your administrator has disabled systeminformations.'; + } + ?> +

          +
          +
        • +
        \ No newline at end of file diff --git a/core/templates/layout.guest.php b/core/templates/layout.guest.php index 7ba7abdbf1..4cdbfd16e5 100644 --- a/core/templates/layout.guest.php +++ b/core/templates/layout.guest.php @@ -4,13 +4,19 @@ ownCloud + + + + + + diff --git a/core/templates/layout.user.php b/core/templates/layout.user.php index 25281c452a..2abe4da853 100644 --- a/core/templates/layout.user.php +++ b/core/templates/layout.user.php @@ -4,14 +4,20 @@ <?php echo isset($_['application']) && !empty($_['application'])?$_['application'].' | ':'' ?>ownCloud <?php echo OC_User::getUser()?' ('.OC_User::getUser().') ':'' ?> + + + + + + @@ -27,6 +33,7 @@ -
          +
            -
          • data-id=""> - +
          • data-id="" + data-type="" data-installed="1"> + - 3rd party' ?> + 3rd party' ?>
          +

          t('Select an App');?>

          - + + +
          diff --git a/settings/templates/help.php b/settings/templates/help.php index f9eb861597..a53ec76d68 100644 --- a/settings/templates/help.php +++ b/settings/templates/help.php @@ -1,5 +1,5 @@ diff --git a/settings/templates/personal.php b/settings/templates/personal.php index 014996a5b2..ee40120d72 100644 --- a/settings/templates/personal.php +++ b/settings/templates/personal.php @@ -55,10 +55,5 @@ echo $form; };?> -

          - ownCloud ()
          - Developed by the ownCloud community, the source code is freely licensed under the AGPL. -

          - diff --git a/settings/templates/users.php b/settings/templates/users.php index ea3fe777ff..5298237f67 100644 --- a/settings/templates/users.php +++ b/settings/templates/users.php @@ -1,20 +1,27 @@ - * This file is licensed under the Affero General Public License version 3 or later. * See the COPYING-README file. */ - $allGroups=array(); foreach($_["groups"] as $group) { - $allGroups[]=$group['name']; + $allGroups[] = $group['name']; } +$_['subadmingroups'] = $allGroups; +$items = array_flip($_['subadmingroups']); +unset($items['admin']); +$_['subadmingroups'] = array_flip($items); ?> - +
          - @@ -48,16 +56,29 @@ foreach($_["groups"] as $group) { ... + + + +
          +
          + + + + @@ -68,9 +89,10 @@ foreach($_["groups"] as $group) { + + + -
          t('Name')?> t( 'Password' ); ?> t( 'Groups' ); ?>t('Group Admin'); ?> t( 'Quota' ); ?>  
          ●●●●●●● set new password + alt="set new password" title="set new password"/> +
          Delete + + + + + +
          + + + +

          + \ No newline at end of file diff --git a/settings/trans.png b/settings/trans.png index e6920168bf..ef57510d53 100644 Binary files a/settings/trans.png and b/settings/trans.png differ diff --git a/settings/users.php b/settings/users.php index 96515a90ce..6f39059757 100644 --- a/settings/users.php +++ b/settings/users.php @@ -6,22 +6,35 @@ */ require_once('../lib/base.php'); -OC_Util::checkAdminUser(); +OC_Util::checkSubAdminUser(); // We have some javascript foo! OC_Util::addScript( 'settings', 'users' ); OC_Util::addScript( 'core', 'multiselect' ); +// TODO Move script to core +OC_Util::addScript('contacts', 'jquery.inview'); OC_Util::addStyle( 'settings', 'settings' ); OC_App::setActiveNavigationEntry( 'core_users' ); $users = array(); $groups = array(); -foreach( OC_User::getUsers() as $i ){ - $users[] = array( "name" => $i, "groups" => join( ", ", OC_Group::getUserGroups( $i ) ),'quota'=>OC_Preferences::getValue($i,'files','quota','default')); +$isadmin = OC_Group::inGroup(OC_User::getUser(),'admin')?true:false; +if($isadmin){ + $accessiblegroups = OC_Group::getGroups(); + $accessibleusers = OC_User::getUsers('', 30); + $subadmins = OC_SubAdmin::getAllSubAdmins(); +}else{ + $accessiblegroups = OC_SubAdmin::getSubAdminsGroups(OC_User::getUser()); + $accessibleusers = OC_Group::usersInGroups($accessiblegroups, '', 30); + $subadmins = false; } -foreach( OC_Group::getGroups() as $i ){ +foreach($accessibleusers as $i){ + $users[] = array( "name" => $i, "groups" => join( ", ", /*array_intersect(*/OC_Group::getUserGroups($i)/*, OC_SubAdmin::getSubAdminsGroups(OC_User::getUser()))*/),'quota'=>OC_Preferences::getValue($i,'files','quota','default'),'subadmin'=>implode(', ',OC_SubAdmin::getSubAdminsGroups($i))); +} + +foreach( $accessiblegroups as $i ){ // Do some more work here soon $groups[] = array( "name" => $i ); } @@ -33,12 +46,21 @@ foreach($quotaPreset as &$preset){ $defaultQuota=OC_Appconfig::getValue('files','default_quota','none'); +$shareNotice = ''; + +if (\OC_App::isEnabled( "files_sharing" ) ) { + + $shareNotice = 'Note: users may only share to groups that they belong to, and their members'; + +} + $tmpl = new OC_Template( "settings", "users", "user" ); $tmpl->assign( "users", $users ); $tmpl->assign( "groups", $groups ); +$tmpl->assign( 'isadmin', (int) $isadmin); +$tmpl->assign( 'subadmins', $subadmins); +$tmpl->assign( 'numofgroups', count($accessiblegroups)); $tmpl->assign( 'quota_preset', $quotaPreset); $tmpl->assign( 'default_quota', $defaultQuota); +$tmpl->assign( 'share_notice', $shareNotice); $tmpl->printPage(); - -?> - diff --git a/status.php b/status.php index 81f339fa53..f314a3a1c6 100644 --- a/status.php +++ b/status.php @@ -4,7 +4,7 @@ * ownCloud status page. usefull if you want to check from the outside if an owncloud installation exists * * @author Frank Karlitschek -* @copyright 2010 Frank Karlitschek karlitschek@kde.org +* @copyright 2012 Frank Karlitschek frank@owncloud.org * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -29,6 +29,3 @@ if(OC_Config::getValue('installed')==1) $installed='true'; else $installed='fals $values=array('installed'=>$installed,'version'=>implode('.',OC_Util::getVersion()),'versionstring'=>OC_Util::getVersionString(),'edition'=>OC_Util::getEditionString()); echo(json_encode($values)); - - -?> diff --git a/tests/data/logo-wide.png b/tests/data/logo-wide.png index b2c16a0f60..648cb7e615 100644 Binary files a/tests/data/logo-wide.png and b/tests/data/logo-wide.png differ diff --git a/tests/index.php b/tests/index.php index ad98048a68..0be27ee3fd 100644 --- a/tests/index.php +++ b/tests/index.php @@ -26,33 +26,64 @@ require_once 'simpletest/mock_objects.php'; require_once 'simpletest/collector.php'; require_once 'simpletest/default_reporter.php'; +$testSuiteName="ownCloud Unit Test Suite"; + +// prepare the reporter +if(OC::$CLI){ + $reporter=new TextReporter; + $test=isset($_SERVER['argv'][1])?$_SERVER['argv'][1]:false; + if($test=='xml'){ + $reporter= new XmlReporter; + $test=false; + + if(isset($_SERVER['argv'][2])){ + $testSuiteName=$testSuiteName." (".$_SERVER['argv'][2].")"; + } + } +}else{ + $reporter=new HtmlReporter; + $test=isset($_GET['test'])?$_GET['test']:false; +} + +// test suite instance +$testSuite=new TestSuite($testSuiteName); + //load core test cases -loadTests(dirname(__FILE__)); +loadTests(dirname(__FILE__), $testSuite, $test); //load app test cases + +// +// TODO: define a list of apps to be enabled + enable them +// + $apps=OC_App::getEnabledApps(); foreach($apps as $app){ if(is_dir(OC::$SERVERROOT.'/apps/'.$app.'/tests')){ - loadTests(OC::$SERVERROOT.'/apps/'.$app.'/tests'); + loadTests(OC::$SERVERROOT.'/apps/'.$app.'/tests', $testSuite, $test); } } -function loadTests($dir=''){ - $test=isset($_GET['test'])?$_GET['test']:false; +// run the suite +if($testSuite->getSize()>0){ + $testSuite->run($reporter); +} + +// helper below +function loadTests($dir,$testSuite, $test){ if($dh=opendir($dir)){ while($name=readdir($dh)){ if($name[0]!='.'){//no hidden files, '.' or '..' $file=$dir.'/'.$name; if(is_dir($file)){ - loadTests($file); + loadTests($file, $testSuite, $test); }elseif(substr($file,-4)=='.php' and $file!=__FILE__){ $name=getTestName($file); if($test===false or $test==$name or substr($name,0,strlen($test))==$test){ - $testCase=new TestSuite($name); - $testCase->addFile($file); - if($testCase->getSize()>0){ - $testCase->run(new HtmlReporter()); - } + $extractor = new SimpleFileLoader(); + $loadedSuite=$extractor->load($file); + if ($loadedSuite->getSize() > 0) + $testSuite->add($loadedSuite); } } } diff --git a/tests/lib/archive.php b/tests/lib/archive.php index 1779127c93..1711be58e0 100644 --- a/tests/lib/archive.php +++ b/tests/lib/archive.php @@ -130,4 +130,12 @@ abstract class Test_Archive extends UnitTestCase { $this->instance->remove('target.txt'); $this->assertFalse($this->instance->fileExists('target.txt')); } + public function testRecursive(){ + $dir=OC::$SERVERROOT.'/apps/files_archive/tests/data'; + $this->instance=$this->getNew(); + $this->instance->addRecursive('/dir',$dir); + $this->assertTrue($this->instance->fileExists('/dir/lorem.txt')); + $this->assertTrue($this->instance->fileExists('/dir/data.zip')); + $this->assertTrue($this->instance->fileExists('/dir/data.tar.gz')); + } } diff --git a/tests/lib/cache.php b/tests/lib/cache.php new file mode 100644 index 0000000000..511999be95 --- /dev/null +++ b/tests/lib/cache.php @@ -0,0 +1,79 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +abstract class Test_Cache extends UnitTestCase { + /** + * @var OC_Cache cache; + */ + protected $instance; + + public function tearDown(){ + $this->instance->clear(); + } + + function testSimple(){ + $this->assertNull($this->instance->get('value1')); + $this->assertFalse($this->instance->hasKey('value1')); + + $value='foobar'; + $this->instance->set('value1',$value); + $this->assertTrue($this->instance->hasKey('value1')); + $received=$this->instance->get('value1'); + $this->assertEqual($value,$received,'Value recieved from cache not equal to the original'); + $value='ipsum lorum'; + $this->instance->set('value1',$value); + $received=$this->instance->get('value1'); + $this->assertEqual($value,$received,'Value not overwritten by second set'); + + $value2='foobar'; + $this->instance->set('value2',$value2); + $received2=$this->instance->get('value2'); + $this->assertTrue($this->instance->hasKey('value1')); + $this->assertTrue($this->instance->hasKey('value2')); + $this->assertEqual($value,$received,'Value changed while setting other variable'); + $this->assertEqual($value2,$received2,'Second value not equal to original'); + + $this->assertFalse($this->instance->hasKey('not_set')); + $this->assertNull($this->instance->get('not_set'),'Unset value not equal to null'); + + $this->assertTrue($this->instance->remove('value1')); + $this->assertFalse($this->instance->hasKey('value1')); + } + + function testClear(){ + $value='ipsum lorum'; + $this->instance->set('1_value1',$value); + $this->instance->set('1_value2',$value); + $this->instance->set('2_value1',$value); + $this->instance->set('3_value1',$value); + + $this->assertTrue($this->instance->clear('1_')); + $this->assertFalse($this->instance->hasKey('1_value1')); + $this->assertFalse($this->instance->hasKey('1_value2')); + $this->assertTrue($this->instance->hasKey('2_value1')); + $this->assertTrue($this->instance->hasKey('3_value1')); + + $this->assertTrue($this->instance->clear()); + $this->assertFalse($this->instance->hasKey('1_value1')); + $this->assertFalse($this->instance->hasKey('1_value2')); + $this->assertFalse($this->instance->hasKey('2_value1')); + $this->assertFalse($this->instance->hasKey('3_value1')); + } + + function testTTL(){ + $value='foobar'; + $this->instance->set('value1',$value,1); + $value2='foobar'; + $this->instance->set('value2',$value2); + sleep(2); + $this->assertFalse($this->instance->hasKey('value1')); + $this->assertNull($this->instance->get('value1')); + $this->assertTrue($this->instance->hasKey('value2')); + $this->assertEqual($value2,$this->instance->get('value2')); + } +} diff --git a/tests/lib/cache/apc.php b/tests/lib/cache/apc.php new file mode 100644 index 0000000000..ab8eb18866 --- /dev/null +++ b/tests/lib/cache/apc.php @@ -0,0 +1,36 @@ +. +* +*/ + +class Test_Cache_APC extends Test_Cache { + function skip() { + $this->skipUnless(function_exists('apc_store')); + } + + public function setUp(){ + $this->instance=new OC_Cache_APC(); + } + + function testTTL(){ + // ttl doesn't work correctly in the same request + // see https://bugs.php.net/bug.php?id=58084 + } +} diff --git a/tests/lib/cache/file.php b/tests/lib/cache/file.php new file mode 100644 index 0000000000..28778efb68 --- /dev/null +++ b/tests/lib/cache/file.php @@ -0,0 +1,63 @@ +. +* +*/ + +class Test_Cache_File extends Test_Cache { + private $user; + + function skip() { + //$this->skipUnless(OC_User::isLoggedIn()); + } + + public function setUp(){ + //clear all proxies and hooks so we can do clean testing + OC_FileProxy::clearProxies(); + OC_Hook::clear('OC_Filesystem'); + + //enable only the encryption hook if needed + if(OC_App::isEnabled('files_encryption')){ + OC_FileProxy::register(new OC_FileProxy_Encryption()); + } + + //set up temporary storage + OC_Filesystem::clearMounts(); + OC_Filesystem::mount('OC_Filestorage_Temporary',array(),'/'); + + OC_User::clearBackends(); + OC_User::useBackend(new OC_User_Dummy()); + + //login + OC_User::createUser('test', 'test'); + + $this->user=OC_User::getUser(); + OC_User::setUserId('test'); + + //set up the users dir + $rootView=new OC_FilesystemView(''); + $rootView->mkdir('/test'); + + $this->instance=new OC_Cache_File(); + } + + public function tearDown(){ + OC_User::setUserId($this->user); + } +} diff --git a/tests/lib/cache/xcache.php b/tests/lib/cache/xcache.php new file mode 100644 index 0000000000..cc2077076c --- /dev/null +++ b/tests/lib/cache/xcache.php @@ -0,0 +1,35 @@ +. +* +*/ + +class Test_Cache_XCache extends Test_Cache { + function skip() { + $this->skipUnless(function_exists('xcache_get')); + } + + public function setUp(){ + $this->instance=new OC_Cache_XCache(); + } + + function testTTL(){ + // ttl doesn't work correctly in the same request + } +} diff --git a/tests/lib/filestorage.php b/tests/lib/filestorage.php index f71b658253..0a8715600d 100644 --- a/tests/lib/filestorage.php +++ b/tests/lib/filestorage.php @@ -31,13 +31,13 @@ abstract class Test_FileStorage extends UnitTestCase { */ public function testRoot(){ $this->assertTrue($this->instance->file_exists('/'),'Root folder does not exist'); - $this->assertTrue($this->instance->is_readable('/'),'Root folder is not readable'); + $this->assertTrue($this->instance->isReadable('/'),'Root folder is not readable'); $this->assertTrue($this->instance->is_dir('/'),'Root folder is not a directory'); $this->assertFalse($this->instance->is_file('/'),'Root folder is a file'); $this->assertEqual('dir',$this->instance->filetype('/')); //without this, any further testing would be useless, not an acutal requirement for filestorage though - $this->assertTrue($this->instance->is_writable('/'),'Root folder is not writable'); + $this->assertTrue($this->instance->isUpdatable('/'),'Root folder is not writable'); } public function testDirectories(){ @@ -50,8 +50,8 @@ abstract class Test_FileStorage extends UnitTestCase { $this->assertFalse($this->instance->is_file('/folder')); $this->assertEqual('dir',$this->instance->filetype('/folder')); $this->assertEqual(0,$this->instance->filesize('/folder')); - $this->assertTrue($this->instance->is_readable('/folder')); - $this->assertTrue($this->instance->is_writable('/folder')); + $this->assertTrue($this->instance->isReadable('/folder')); + $this->assertTrue($this->instance->isUpdatable('/folder')); $dh=$this->instance->opendir('/'); $content=array(); @@ -129,19 +129,32 @@ abstract class Test_FileStorage extends UnitTestCase { $this->assertEqual(file_get_contents($textFile),$this->instance->file_get_contents('/target.txt')); } - public function testLocalFile(){ + public function testLocal(){ $textFile=OC::$SERVERROOT.'/tests/data/lorem.txt'; $this->instance->file_put_contents('/lorem.txt',file_get_contents($textFile)); $localFile=$this->instance->getLocalFile('/lorem.txt'); $this->assertTrue(file_exists($localFile)); $this->assertEqual(file_get_contents($localFile),file_get_contents($textFile)); + + $this->instance->mkdir('/folder'); + $this->instance->file_put_contents('/folder/lorem.txt',file_get_contents($textFile)); + $this->instance->file_put_contents('/folder/bar.txt','asd'); + $this->instance->mkdir('/folder/recursive'); + $this->instance->file_put_contents('/folder/recursive/file.txt','foo'); + $localFolder=$this->instance->getLocalFolder('/folder'); + + $this->assertTrue(is_dir($localFolder)); + $this->assertTrue(file_exists($localFolder.'/lorem.txt')); + $this->assertEqual(file_get_contents($localFolder.'/lorem.txt'),file_get_contents($textFile)); + $this->assertEqual(file_get_contents($localFolder.'/bar.txt'),'asd'); + $this->assertEqual(file_get_contents($localFolder.'/recursive/file.txt'),'foo'); } public function testStat(){ $textFile=OC::$SERVERROOT.'/tests/data/lorem.txt'; $ctimeStart=time(); $this->instance->file_put_contents('/lorem.txt',file_get_contents($textFile)); - $this->assertTrue($this->instance->is_readable('/lorem.txt')); + $this->assertTrue($this->instance->isReadable('/lorem.txt')); $ctimeEnd=time(); $cTime=$this->instance->filectime('/lorem.txt'); $mTime=$this->instance->filemtime('/lorem.txt'); @@ -149,6 +162,9 @@ abstract class Test_FileStorage extends UnitTestCase { $this->assertTrue(($ctimeStart-1)<=$cTime); $this->assertTrue($cTime<=($ctimeEnd+1)); } + $this->assertTrue($this->instance->hasUpdated('/lorem.txt',$ctimeStart-1)); + $this->assertTrue($this->instance->hasUpdated('/',$ctimeStart-1)); + $this->assertTrue(($ctimeStart-1)<=$mTime); $this->assertTrue($mTime<=($ctimeEnd+1)); $this->assertEqual(filesize($textFile),$this->instance->filesize('/lorem.txt')); @@ -168,6 +184,8 @@ abstract class Test_FileStorage extends UnitTestCase { $this->assertTrue(($mtimeStart-1)<=$mTime); $this->assertTrue($mTime<=($mtimeEnd+1)); $this->assertEqual($cTime,$originalCTime); + + $this->assertTrue($this->instance->hasUpdated('/lorem.txt',$mtimeStart-1)); if($this->instance->touch('/lorem.txt',100)!==false){ $mTime=$this->instance->filemtime('/lorem.txt'); @@ -184,6 +202,9 @@ abstract class Test_FileStorage extends UnitTestCase { $mTime=$this->instance->filemtime('/lorem.txt'); $this->assertTrue(($mtimeStart-1)<=$mTime); $this->assertTrue($mTime<=($mtimeEnd+1)); + + $this->instance->unlink('/lorem.txt'); + $this->assertTrue($this->instance->hasUpdated('/',$mtimeStart-1)); } public function testSearch(){ diff --git a/tests/lib/filesystem.php b/tests/lib/filesystem.php index 3e28d8c06e..72af083578 100644 --- a/tests/lib/filesystem.php +++ b/tests/lib/filesystem.php @@ -59,6 +59,18 @@ class Test_Filesystem extends UnitTestCase{ $this->assertEqual('/',OC_Filesystem::getMountPoint('/some')); $this->assertEqual('folder',OC_Filesystem::getInternalPath('/some/folder')); } + + public function testNormalize(){ + $this->assertEqual('/path',OC_Filesystem::normalizePath('/path/')); + $this->assertEqual('/path/',OC_Filesystem::normalizePath('/path/',false)); + $this->assertEqual('/path',OC_Filesystem::normalizePath('path')); + $this->assertEqual('/path',OC_Filesystem::normalizePath('\path')); + $this->assertEqual('/foo/bar',OC_Filesystem::normalizePath('/foo//bar/')); + $this->assertEqual('/foo/bar',OC_Filesystem::normalizePath('/foo////bar')); + if(class_exists('Normalizer')){ + $this->assertEqual("/foo/bar\xC3\xBC",OC_Filesystem::normalizePath("/foo/baru\xCC\x88")); + } + } } ?> \ No newline at end of file diff --git a/tests/lib/share/backend.php b/tests/lib/share/backend.php new file mode 100644 index 0000000000..4ee59963d5 --- /dev/null +++ b/tests/lib/share/backend.php @@ -0,0 +1,69 @@ +. +*/ + +class Test_Share_Backend implements OCP\Share_Backend { + + const FORMAT_SOURCE = 0; + const FORMAT_TARGET = 1; + const FORMAT_PERMISSIONS = 2; + + private $testItem1 = 'test.txt'; + private $testItem2 = 'share.txt'; + + public function isValidSource($itemSource, $uidOwner) { + if ($itemSource == $this->testItem1 || $itemSource == $this->testItem2) { + return true; + } + } + + public function generateTarget($itemSource, $shareWith, $exclude = null) { + // Always make target be test.txt to cause conflicts + $target = 'test.txt'; + if (isset($exclude)) { + $pos = strrpos($target, '.'); + $name = substr($target, 0, $pos); + $ext = substr($target, $pos); + $append = ''; + $i = 1; + while (in_array($name.$append.$ext, $exclude)) { + $append = $i; + $i++; + } + $target = $name.$append.$ext; + } + return $target; + } + + public function formatItems($items, $format, $parameters = null) { + $testItems = array(); + foreach ($items as $item) { + if ($format == self::FORMAT_SOURCE) { + $testItems[] = $item['item_source']; + } else if ($format == self::FORMAT_TARGET) { + $testItems[] = $item['item_target']; + } else if ($format == self::FORMAT_PERMISSIONS) { + $testItems[] = $item['permissions']; + } + } + return $testItems; + } + +} diff --git a/tests/lib/share/share.php b/tests/lib/share/share.php new file mode 100644 index 0000000000..785591829d --- /dev/null +++ b/tests/lib/share/share.php @@ -0,0 +1,380 @@ +. +*/ + +class Test_Share extends UnitTestCase { + + protected $itemType; + protected $userBackend; + protected $user1; + protected $user2; + protected $groupBackend; + protected $group1; + protected $group2; + + + public function setUp() { + OC_User::clearBackends(); + OC_User::useBackend('dummy'); + $this->user1 = uniqid('user_'); + $this->user2 = uniqid('user_'); + $this->user3 = uniqid('user_'); + $this->user4 = uniqid('user_'); + OC_User::createUser($this->user1, 'pass'); + OC_User::createUser($this->user2, 'pass'); + OC_User::createUser($this->user3, 'pass'); + OC_User::createUser($this->user4, 'pass'); + OC_User::setUserId($this->user1); + OC_Group::clearBackends(); + OC_Group::useBackend(new OC_Group_Dummy); + $this->group1 = uniqid('group_'); + $this->group2 = uniqid('group_'); + OC_Group::createGroup($this->group1); + OC_Group::createGroup($this->group2); + OC_Group::addToGroup($this->user1, $this->group1); + OC_Group::addToGroup($this->user2, $this->group1); + OC_Group::addToGroup($this->user3, $this->group1); + OC_Group::addToGroup($this->user2, $this->group2); + OC_Group::addToGroup($this->user4, $this->group2); + OCP\Share::registerBackend('test', 'Test_Share_Backend'); + } + + public function tearDown() { + $query = OC_DB::prepare('DELETE FROM *PREFIX*share WHERE item_type = ?'); + $query->execute(array('test')); + } + + public function testShareInvalidShareType() { + $this->expectException(new Exception('Share type foobar is not valid for test.txt')); + OCP\Share::shareItem('test', 'test.txt', 'foobar', $this->user2, OCP\Share::PERMISSION_READ); + } + + public function testInvalidItemType() { + $message = 'Sharing backend for foobar not found'; + try { + OCP\Share::shareItem('foobar', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user2, OCP\Share::PERMISSION_READ); + $this->fail('Exception was expected: '.$message); + } catch (Exception $exception) { + $this->assertEqual($exception->getMessage(), $message); + } + try { + OCP\Share::getItemsSharedWith('foobar'); + $this->fail('Exception was expected: '.$message); + } catch (Exception $exception) { + $this->assertEqual($exception->getMessage(), $message); + } + try { + OCP\Share::getItemSharedWith('foobar', 'test.txt'); + $this->fail('Exception was expected: '.$message); + } catch (Exception $exception) { + $this->assertEqual($exception->getMessage(), $message); + } + try { + OCP\Share::getItemSharedWithBySource('foobar', 'test.txt'); + $this->fail('Exception was expected: '.$message); + } catch (Exception $exception) { + $this->assertEqual($exception->getMessage(), $message); + } + try { + OCP\Share::getItemShared('foobar', 'test.txt'); + $this->fail('Exception was expected: '.$message); + } catch (Exception $exception) { + $this->assertEqual($exception->getMessage(), $message); + } + try { + OCP\Share::unshare('foobar', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user2); + $this->fail('Exception was expected: '.$message); + } catch (Exception $exception) { + $this->assertEqual($exception->getMessage(), $message); + } + try { + OCP\Share::setPermissions('foobar', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user2, OCP\Share::PERMISSION_UPDATE); + $this->fail('Exception was expected: '.$message); + } catch (Exception $exception) { + $this->assertEqual($exception->getMessage(), $message); + } + } + + public function testShareWithUser() { + // Invalid shares + $message = 'Sharing test.txt failed, because the user '.$this->user1.' is the item owner'; + try { + OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user1, OCP\Share::PERMISSION_READ); + $this->fail('Exception was expected: '.$message); + } catch (Exception $exception) { + $this->assertEqual($exception->getMessage(), $message); + } + $message = 'Sharing test.txt failed, because the user foobar does not exist'; + try { + OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, 'foobar', OCP\Share::PERMISSION_READ); + $this->fail('Exception was expected: '.$message); + } catch (Exception $exception) { + $this->assertEqual($exception->getMessage(), $message); + } + $message = 'Sharing foobar failed, because the sharing backend for test could not find its source'; + try { + OCP\Share::shareItem('test', 'foobar', OCP\Share::SHARE_TYPE_USER, $this->user2, OCP\Share::PERMISSION_READ); + $this->fail('Exception was expected: '.$message); + } catch (Exception $exception) { + $this->assertEqual($exception->getMessage(), $message); + } + + // Valid share + $this->assertTrue(OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user2, OCP\Share::PERMISSION_READ)); + $this->assertEqual(OCP\Share::getItemShared('test', 'test.txt', Test_Share_Backend::FORMAT_SOURCE), array('test.txt')); + OC_User::setUserId($this->user2); + $this->assertEqual(OCP\Share::getItemSharedWith('test', 'test.txt', Test_Share_Backend::FORMAT_SOURCE), array('test.txt')); + + // Attempt to share again + OC_User::setUserId($this->user1); + $message = 'Sharing test.txt failed, because this item is already shared with '.$this->user2; + try { + OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user2, OCP\Share::PERMISSION_READ); + $this->fail('Exception was expected: '.$message); + } catch (Exception $exception) { + $this->assertEqual($exception->getMessage(), $message); + } + + // Attempt to share back + OC_User::setUserId($this->user2); + $message = 'Sharing test.txt failed, because the user '.$this->user1.' is the original sharer'; + try { + OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user1, OCP\Share::PERMISSION_READ); + $this->fail('Exception was expected: '.$message); + } catch (Exception $exception) { + $this->assertEqual($exception->getMessage(), $message); + } + + // Unshare + OC_User::setUserId($this->user1); + $this->assertTrue(OCP\Share::unshare('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user2)); + + // Attempt reshare without share permission + $this->assertTrue(OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user2, OCP\Share::PERMISSION_READ)); + OC_User::setUserId($this->user2); + $message = 'Sharing test.txt failed, because resharing is not allowed'; + try { + OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user3, OCP\Share::PERMISSION_READ); + $this->fail('Exception was expected: '.$message); + } catch (Exception $exception) { + $this->assertEqual($exception->getMessage(), $message); + } + + // Owner grants share and update permission + OC_User::setUserId($this->user1); + $this->assertTrue(OCP\Share::setPermissions('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user2, OCP\Share::PERMISSION_READ | OCP\Share::PERMISSION_UPDATE | OCP\Share::PERMISSION_SHARE)); + + // Attempt reshare with escalated permissions + OC_User::setUserId($this->user2); + $message = 'Sharing test.txt failed, because the permissions exceed permissions granted to '.$this->user2; + try { + OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user3, OCP\Share::PERMISSION_READ | OCP\Share::PERMISSION_DELETE); + $this->fail('Exception was expected: '.$message); + } catch (Exception $exception) { + $this->assertEqual($exception->getMessage(), $message); + } + + // Valid reshare + $this->assertTrue(OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user3, OCP\Share::PERMISSION_READ | OCP\Share::PERMISSION_UPDATE)); + $this->assertEqual(OCP\Share::getItemShared('test', 'test.txt', Test_Share_Backend::FORMAT_SOURCE), array('test.txt')); + OC_User::setUserId($this->user3); + $this->assertEqual(OCP\Share::getItemSharedWith('test', 'test.txt', Test_Share_Backend::FORMAT_SOURCE), array('test.txt')); + $this->assertEqual(OCP\Share::getItemSharedWith('test', 'test.txt', Test_Share_Backend::FORMAT_PERMISSIONS), array(OCP\Share::PERMISSION_READ | OCP\Share::PERMISSION_UPDATE)); + + // Attempt to escalate permissions + OC_User::setUserId($this->user2); + $message = 'Setting permissions for test.txt failed, because the permissions exceed permissions granted to '.$this->user2; + try { + OCP\Share::setPermissions('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user3, OCP\Share::PERMISSION_READ | OCP\Share::PERMISSION_DELETE); + $this->fail('Exception was expected: '.$message); + } catch (Exception $exception) { + $this->assertEqual($exception->getMessage(), $message); + } + + // Remove update permission + OC_User::setUserId($this->user1); + $this->assertTrue(OCP\Share::setPermissions('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user2, OCP\Share::PERMISSION_READ | OCP\Share::PERMISSION_SHARE)); + OC_User::setUserId($this->user2); + $this->assertEqual(OCP\Share::getItemSharedWith('test', 'test.txt', Test_Share_Backend::FORMAT_PERMISSIONS), array(OCP\Share::PERMISSION_READ | OCP\Share::PERMISSION_SHARE)); + OC_User::setUserId($this->user3); + $this->assertEqual(OCP\Share::getItemSharedWith('test', 'test.txt', Test_Share_Backend::FORMAT_PERMISSIONS), array(OCP\Share::PERMISSION_READ)); + + // Remove share permission + OC_User::setUserId($this->user1); + $this->assertTrue(OCP\Share::setPermissions('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user2, OCP\Share::PERMISSION_READ)); + OC_User::setUserId($this->user2); + $this->assertEqual(OCP\Share::getItemSharedWith('test', 'test.txt', Test_Share_Backend::FORMAT_PERMISSIONS), array(OCP\Share::PERMISSION_READ)); + OC_User::setUserId($this->user3); + $this->assertFalse(OCP\Share::getItemSharedWith('test', 'test.txt')); + + // Reshare again, and then have owner unshare + OC_User::setUserId($this->user1); + $this->assertTrue(OCP\Share::setPermissions('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user2, OCP\Share::PERMISSION_READ | OCP\Share::PERMISSION_SHARE)); + OC_User::setUserId($this->user2); + $this->assertTrue(OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user3, OCP\Share::PERMISSION_READ)); + OC_User::setUserId($this->user1); + $this->assertTrue(OCP\Share::unshare('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user2)); + OC_User::setUserId($this->user2); + $this->assertFalse(OCP\Share::getItemSharedWith('test', 'test.txt')); + OC_User::setUserId($this->user3); + $this->assertFalse(OCP\Share::getItemSharedWith('test', 'test.txt')); + + // Attempt target conflict + OC_User::setUserId($this->user1); + $this->assertTrue(OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user2, OCP\Share::PERMISSION_READ)); + OC_User::setUserId($this->user3); + $this->assertTrue(OCP\Share::shareItem('test', 'share.txt', OCP\Share::SHARE_TYPE_USER, $this->user2, OCP\Share::PERMISSION_READ)); + OC_User::setUserId($this->user2); + $this->assertEqual(OCP\Share::getItemsSharedWith('test', Test_Share_Backend::FORMAT_TARGET), array('test.txt', 'test1.txt')); + + // Remove user + OC_User::deleteUser($this->user1); + OC_User::setUserId($this->user2); + $this->assertEqual(OCP\Share::getItemsSharedWith('test', Test_Share_Backend::FORMAT_TARGET), array('test1.txt')); + } + + public function testShareWithGroup() { + // Invalid shares + $message = 'Sharing test.txt failed, because the group foobar does not exist'; + try { + OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_GROUP, 'foobar', OCP\Share::PERMISSION_READ); + $this->fail('Exception was expected: '.$message); + } catch (Exception $exception) { + $this->assertEqual($exception->getMessage(), $message); + } + $message = 'Sharing test.txt failed, because '.$this->user1.' is not a member of the group '.$this->group2; + try { + OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_GROUP, $this->group2, OCP\Share::PERMISSION_READ); + $this->fail('Exception was expected: '.$message); + } catch (Exception $exception) { + $this->assertEqual($exception->getMessage(), $message); + } + + // Valid share + $this->assertTrue(OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_GROUP, $this->group1, OCP\Share::PERMISSION_READ)); + $this->assertEqual(OCP\Share::getItemShared('test', 'test.txt', Test_Share_Backend::FORMAT_SOURCE), array('test.txt')); + OC_User::setUserId($this->user2); + $this->assertEqual(OCP\Share::getItemSharedWith('test', 'test.txt', Test_Share_Backend::FORMAT_SOURCE), array('test.txt')); + OC_User::setUserId($this->user3); + $this->assertEqual(OCP\Share::getItemSharedWith('test', 'test.txt', Test_Share_Backend::FORMAT_SOURCE), array('test.txt')); + + // Attempt to share again + OC_User::setUserId($this->user1); + $message = 'Sharing test.txt failed, because this item is already shared with '.$this->group1; + try { + OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_GROUP, $this->group1, OCP\Share::PERMISSION_READ); + $this->fail('Exception was expected: '.$message); + } catch (Exception $exception) { + $this->assertEqual($exception->getMessage(), $message); + } + + // Attempt to share back to owner of group share + OC_User::setUserId($this->user2); + $message = 'Sharing test.txt failed, because the user '.$this->user1.' is the original sharer'; + try { + OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user1, OCP\Share::PERMISSION_READ); + $this->fail('Exception was expected: '.$message); + } catch (Exception $exception) { + $this->assertEqual($exception->getMessage(), $message); + } + + // Attempt to share back to group + $message = 'Sharing test.txt failed, because this item is already shared with '.$this->group1; + try { + OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_GROUP, $this->group1, OCP\Share::PERMISSION_READ); + $this->fail('Exception was expected: '.$message); + } catch (Exception $exception) { + $this->assertEqual($exception->getMessage(), $message); + } + + // Attempt to share back to member of group + $message ='Sharing test.txt failed, because this item is already shared with '.$this->user3; + try { + OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user3, OCP\Share::PERMISSION_READ); + $this->fail('Exception was expected: '.$message); + } catch (Exception $exception) { + $this->assertEqual($exception->getMessage(), $message); + } + + // Unshare + OC_User::setUserId($this->user1); + $this->assertTrue(OCP\Share::unshare('test', 'test.txt', OCP\Share::SHARE_TYPE_GROUP, $this->group1)); + + // Valid share with same person - user then group + $this->assertTrue(OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user2, OCP\Share::PERMISSION_READ | OCP\Share::PERMISSION_DELETE | OCP\Share::PERMISSION_SHARE)); + $this->assertTrue(OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_GROUP, $this->group1, OCP\Share::PERMISSION_READ | OCP\Share::PERMISSION_UPDATE)); + OC_User::setUserId($this->user2); + $this->assertEqual(OCP\Share::getItemsSharedWith('test', Test_Share_Backend::FORMAT_TARGET), array('test.txt')); + $this->assertEqual(OCP\Share::getItemSharedWith('test', 'test.txt', Test_Share_Backend::FORMAT_PERMISSIONS), array(OCP\Share::PERMISSION_READ | OCP\Share::PERMISSION_UPDATE | OCP\Share::PERMISSION_DELETE | OCP\Share::PERMISSION_SHARE)); + OC_User::setUserId($this->user3); + $this->assertEqual(OCP\Share::getItemsSharedWith('test', Test_Share_Backend::FORMAT_TARGET), array('test.txt')); + $this->assertEqual(OCP\Share::getItemSharedWith('test', 'test.txt', Test_Share_Backend::FORMAT_PERMISSIONS), array(OCP\Share::PERMISSION_READ | OCP\Share::PERMISSION_UPDATE)); + + // Valid reshare + OC_User::setUserId($this->user2); + $this->assertTrue(OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user4, OCP\Share::PERMISSION_READ)); + OC_User::setUserId($this->user4); + $this->assertEqual(OCP\Share::getItemsSharedWith('test', Test_Share_Backend::FORMAT_TARGET), array('test.txt')); + + // Unshare from user only + OC_User::setUserId($this->user1); + $this->assertTrue(OCP\Share::unshare('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user2)); + OC_User::setUserId($this->user2); + $this->assertEqual(OCP\Share::getItemSharedWith('test', 'test.txt', Test_Share_Backend::FORMAT_PERMISSIONS), array(OCP\Share::PERMISSION_READ | OCP\Share::PERMISSION_UPDATE)); + OC_User::setUserId($this->user4); + $this->assertEqual(OCP\Share::getItemsSharedWith('test', Test_Share_Backend::FORMAT_TARGET), array()); + + // Valid share with same person - group then user + OC_User::setUserId($this->user1); + $this->assertTrue(OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user2, OCP\Share::PERMISSION_READ | OCP\Share::PERMISSION_DELETE)); + OC_User::setUserId($this->user2); + $this->assertEqual(OCP\Share::getItemsSharedWith('test', Test_Share_Backend::FORMAT_TARGET), array('test.txt')); + $this->assertEqual(OCP\Share::getItemSharedWith('test', 'test.txt', Test_Share_Backend::FORMAT_PERMISSIONS), array(OCP\Share::PERMISSION_READ | OCP\Share::PERMISSION_UPDATE | OCP\Share::PERMISSION_DELETE)); + + // Unshare from group only + OC_User::setUserId($this->user1); + $this->assertTrue(OCP\Share::unshare('test', 'test.txt', OCP\Share::SHARE_TYPE_GROUP, $this->group1)); + OC_User::setUserId($this->user2); + $this->assertEqual(OCP\Share::getItemSharedWith('test', 'test.txt', Test_Share_Backend::FORMAT_PERMISSIONS), array(OCP\Share::PERMISSION_READ | OCP\Share::PERMISSION_DELETE)); + + // Attempt user specific target conflict + OC_User::setUserId($this->user3); + $this->assertTrue(OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_GROUP, $this->group1, OCP\Share::PERMISSION_READ | OCP\Share::PERMISSION_SHARE)); + OC_User::setUserId($this->user2); + $this->assertEqual(OCP\Share::getItemsSharedWith('test', Test_Share_Backend::FORMAT_TARGET), array('test.txt', 'test1.txt')); + +// // Valid reshare TODO Broken +// $this->assertTrue(OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user4, OCP\Share::PERMISSION_READ | OCP\Share::PERMISSION_SHARE)); +// OC_User::setUserId($this->user4); +// $this->assertEqual(OCP\Share::getItemsSharedWith('test', Test_Share_Backend::FORMAT_TARGET), array('test1.txt')); +// +// // Remove user from group +// OC_Group::removeFromGroup($this->user2, $this->group1); +// OC_User::setUserId($this->user2); +// $this->assertEqual(OCP\Share::getItemsSharedWith('test', Test_Share_Backend::FORMAT_TARGET), array('test.txt')); +// OC_User::setUserId($this->user4); +// $this->assertEqual(OCP\Share::getItemsSharedWith('test', Test_Share_Backend::FORMAT_TARGET), array()); + + // Add user to group + + // Remove group + } + +} diff --git a/tests/lib/user/backend.php b/tests/lib/user/backend.php index 5dab5afb18..984249e84e 100644 --- a/tests/lib/user/backend.php +++ b/tests/lib/user/backend.php @@ -20,6 +20,16 @@ * */ +/** + * Abstract class to provide the basis of backend-specific unit test classes. + * + * All subclasses MUST assign a backend property in setUp() which implements + * user operations (add, remove, etc.). Test methods in this class will then be + * run on each separate subclass and backend therein. + * + * For an example see /tests/lib/user/dummy.php + */ + abstract class Test_User_Backend extends UnitTestCase { /** * @var OC_User_Backend $backend diff --git a/tests/lib/user/database.php b/tests/lib/user/database.php index b2fcce93c5..f484ffa78f 100644 --- a/tests/lib/user/database.php +++ b/tests/lib/user/database.php @@ -21,7 +21,6 @@ */ class Test_User_Database extends Test_User_Backend { - private $user=array(); /** * get a new unique user name * test cases can override this in order to clean up created user diff --git a/webapps.php b/webapps.php deleted file mode 100644 index 99553fa8ef..0000000000 --- a/webapps.php +++ /dev/null @@ -1,56 +0,0 @@ -. -* -*/ - -$RUNTIME_NOAPPS = TRUE; //no apps, yet - -require_once('lib/base.php'); - - -//valid user account -if(isset($_SERVER['PHP_AUTH_USER'])) $authuser=$_SERVER['PHP_AUTH_USER']; else $authuser=''; -if(isset($_SERVER['PHP_AUTH_PW'])) $authpw=$_SERVER['PHP_AUTH_PW']; else $authpw=''; - -if(!OC_User::login($authuser,$authpw)){ - header('WWW-Authenticate: Basic realm="your valid user account"'); - header('HTTP/1.0 401 Unauthorized'); - exit; -}else{ - - $apps=OC_App::getEnabledApps(); - $values=array(); - foreach($apps as $app) { - $info=OC_App::getAppInfo($app); - if(isset($info['standalone'])) { - $newvalue=array('name'=>$info['name'],'url'=>OC_Helper::linkToAbsolute($app,''),'icon'=>''); - $values[]=$newvalue; - } - - } - - echo(json_encode($values)); - exit; - - -} - -?>