From 0f64cd747dba6cd7e7c2151048941f0a2b2abcde Mon Sep 17 00:00:00 2001 From: hexstudy Date: Fri, 15 Jan 2016 17:04:16 +0000 Subject: [PATCH] git-svn-id: https://msi/svn/firstRepo/Service/branches/Manut_2.2.3@49 0f545695-f87b-41b6-9a03-7f16563b5454 --- DropBoxPhp/DropboxClient.php | 672 +++++++++++++++++++++++++++ DropBoxPhp/OAuthSimple.php | 532 +++++++++++++++++++++ DropBoxPhp/sample-form.php | 85 ++++ DropBoxPhp/sample.php | 133 ++++++ DropBoxPhp/test_download_Apps | 1 + DropBoxPhp/tokens/access.token | 1 + Service_Manut.zip | Bin 0 -> 82848 bytes config.inc.php | 4 + image.php | 165 +++---- image_backend.php | 111 +++++ include.php | 6 +- management.php | 14 +- myDropBoxObj.php | 83 ++++ nbproject/private/private.properties | 6 + nbproject/private/private.xml | 7 + nbproject/project.properties | 7 + nbproject/project.xml | 9 + ricette.php | 12 +- serviceapp.php | 5 +- t.php | 178 +++++++ tokens/access.token | 1 + utility.php | 23 + 22 files changed, 1942 insertions(+), 113 deletions(-) create mode 100644 DropBoxPhp/DropboxClient.php create mode 100644 DropBoxPhp/OAuthSimple.php create mode 100644 DropBoxPhp/sample-form.php create mode 100644 DropBoxPhp/sample.php create mode 100644 DropBoxPhp/test_download_Apps create mode 100644 DropBoxPhp/tokens/access.token create mode 100644 Service_Manut.zip create mode 100644 image_backend.php create mode 100644 myDropBoxObj.php create mode 100644 nbproject/private/private.properties create mode 100644 nbproject/private/private.xml create mode 100644 nbproject/project.properties create mode 100644 nbproject/project.xml create mode 100644 t.php create mode 100644 tokens/access.token diff --git a/DropBoxPhp/DropboxClient.php b/DropBoxPhp/DropboxClient.php new file mode 100644 index 0000000..ab66ac0 --- /dev/null +++ b/DropBoxPhp/DropboxClient.php @@ -0,0 +1,672 @@ + + * @copyright Fabian Schlieper 2014 + * @version 1.7.1 + * @license See LICENSE + * + */ + +require_once(dirname(__FILE__)."/OAuthSimple.php"); + +class DropboxClient { + + const API_URL = "https://api.dropbox.com/1/"; + const API_CONTENT_URL = "https://api-content.dropbox.com/1/"; + + const BUFFER_SIZE = 4096; + + const MAX_UPLOAD_CHUNK_SIZE = 150000000; // 150MB + + const UPLOAD_CHUNK_SIZE = 4000000; // 4MB + + private $appParams; + private $consumerToken; + + private $requestToken; + private $accessToken; + + private $locale; + private $rootPath; + + private $useCurl; + + function __construct ($app_params, $locale = "en"){ + $this->appParams = $app_params; + if(empty($app_params['app_key'])) + throw new DropboxException("App Key is empty!"); + + $this->consumerToken = array('t' => $this->appParams['app_key'], 's' => $this->appParams['app_secret']); + $this->locale = $locale; + $this->rootPath = empty($app_params['app_full_access']) ? "sandbox" : "dropbox"; + + $this->requestToken = null; + $this->accessToken = null; + + $this->useCurl = function_exists('curl_init'); + } + + function __wakeup() { + $this->useCurl = $this->useCurl && function_exists('curl_init'); + } + /** + * Sets whether to use cURL if its available or PHP HTTP wrappers otherwise + * + * @access public + * @return boolean Whether to actually use cURL (always false if not installed) + */ + public function SetUseCUrl($use_it) + { + return ($this->useCurl = ($use_it && function_exists('curl_init'))); + } + + // ################################################## + // Authorization + + /** + * Step 1 of authentication process. Retrieves a request token or returns a previously retrieved one. + * + * @access public + * @param boolean $get_new_token Optional (default false). Wether to retrieve a new request token. + * @return array Request Token array. + */ + public function GetRequestToken($get_new_token=false) + { + if(!empty($this->requestToken) && !$get_new_token) + return $this->requestToken; + + $rt = $this->authCall("oauth/request_token"); + if(empty($rt) || empty($rt['oauth_token'])) + throw new DropboxException('Could not get request token!'); + + return ($this->requestToken = array('t'=>$rt['oauth_token'], 's'=>$rt['oauth_token_secret'])); + } + + /** + * Step 2. Returns a URL the user must be redirected to in order to connect the app to their Dropbox account + * + * @access public + * @param string $return_url URL users are redirected after authorization + * @return string URL + */ + public function BuildAuthorizeUrl($return_url) + { + $rt = $this->GetRequestToken(); + if(empty($rt) || empty($rt['t'])) throw new DropboxException('Request Token Invalid ('.print_r($rt,true).').'); + return "https://www.dropbox.com/1/oauth/authorize?oauth_token=".$rt['t']."&oauth_callback=".urlencode($return_url); + } + + /** + * Step 3. Acquires an access token. This is the final step of authentication. + * + * @access public + * @param array $request_token Optional. The previously retrieved request token. This parameter can only be skipped if the DropboxClient object has been (de)serialized. + * @return array Access Token array. + */ + public function GetAccessToken($request_token = null) + { + if(!empty($this->accessToken)) return $this->accessToken; + + if(empty($request_token)) $request_token = $this->requestToken; + if(empty($request_token)) throw new DropboxException('Request token required!'); + + $at = $this->authCall("oauth/access_token", $request_token); + if(empty($at)) + throw new DropboxException(sprintf('Could not get access token! (request token: %s)', $request_token['t'])); + + return ($this->accessToken = array('t'=>$at['oauth_token'], 's'=>$at['oauth_token_secret'])); + } + + /** + * Sets a previously retrieved (and stored) access token. + * + * @access public + * @param string|object $token The Access Token + * @return none + */ + public function SetAccessToken($token) + { + if(empty($token['t']) || empty($token['s'])) throw new DropboxException('Passed invalid access token.'); + $this->accessToken = $token; + } + + /** + * Checks if an access token has been set. + * + * @access public + * @return boolean Authorized or not + */ + public function IsAuthorized() + { + if(empty($this->accessToken)) return false; + return true; + } + + + // ################################################## + // API Functions + + + /** + * Retrieves information about the user's account. + * + * @access public + * @return object Account info object. See https://www.dropbox.com/developers/reference/api#account-info + */ + public function GetAccountInfo() + { + return $this->apiCall("account/info", "GET"); + } + + + /** + * Get file list of a dropbox folder. + * + * @access public + * @param string|object $dropbox_path Dropbox path of the folder + * @return array An array with metadata of files/folders keyed by paths + */ + public function GetFiles($dropbox_path='', $recursive=false, $include_deleted=false) + { + if(is_object($dropbox_path) && !empty($dropbox_path->path)) $dropbox_path = $dropbox_path->path; + return $this->getFileTree($dropbox_path, $include_deleted, $recursive ? 1000 : 0); + } + + /** + * Get file or folder metadata + * + * @access public + * @param $dropbox_path string Dropbox path of the file or folder + */ + public function GetMetadata($dropbox_path, $include_deleted=false, $rev=null) + { + if(is_object($dropbox_path) && !empty($dropbox_path->path)) $dropbox_path = $dropbox_path->path; + return $this->apiCall("metadata/$this->rootPath/$dropbox_path", "GET", compact('include_deleted','rev')); + } + + /** + * Download a file to the webserver + * + * @access public + * @param string|object $dropbox_file Dropbox path or metadata object of the file to download. + * @param string $dest_path Local path for destination + * @param string $rev Optional. The revision of the file to retrieve. This defaults to the most recent revision. + * @param callback $progress_changed_callback Optional. Callback that will be called during download with 2 args: 1. bytes loaded, 2. file size + * @return object Dropbox file metadata + */ + public function DownloadFile($dropbox_file, $dest_path='', $rev=null, $progress_changed_callback = null) + { + if(is_object($dropbox_file) && !empty($dropbox_file->path)) + $dropbox_file = $dropbox_file->path; + + if(empty($dest_path)) $dest_path = basename($dropbox_file); + + $url = $this->cleanUrl(self::API_CONTENT_URL."/files/$this->rootPath/$dropbox_file") + . (!empty($rev) ? ('?'.http_build_query(array('rev' => $rev),'','&')) : ''); + $context = $this->createRequestContext($url, "GET"); + + $fh = @fopen($dest_path, 'wb'); // write binary + if($fh === false) { + @fclose($rh); + throw new DropboxException("Could not create file $dest_path !"); + } + + if($this->useCurl) { + curl_setopt($context, CURLOPT_BINARYTRANSFER, true); + curl_setopt($context, CURLOPT_RETURNTRANSFER, true); + curl_setopt($context, CURLOPT_FILE, $fh); + $response_headers = array(); + self::execCurlAndClose($context, $response_headers); + fclose($fh); + $meta = self::getMetaFromHeaders($response_headers, true); + $bytes_loaded = filesize($dest_path); + } else { + $rh = @fopen($url, 'rb', false, $context); // read binary + if($rh === false) + throw new DropboxException("HTTP request to $url failed!"); + + + // get file meta from HTTP header + $s_meta = stream_get_meta_data($rh); + $meta = self::getMetaFromHeaders($s_meta['wrapper_data'], true); + $bytes_loaded = 0; + while (!feof($rh)) { + if(($s=fwrite($fh, fread($rh, self::BUFFER_SIZE))) === false) { + @fclose($rh); + @fclose($fh); + throw new DropboxException("Writing to file $dest_path failed!'"); + } + $bytes_loaded += $s; + if(!empty($progress_changed_callback)) { + call_user_func($progress_changed_callback, $bytes_loaded, $meta->bytes); + } + } + + fclose($rh); + fclose($fh); + } + + if($meta->bytes != $bytes_loaded) + throw new DropboxException("Download size mismatch! (header:{$meta->bytes} vs actual:{$bytes_loaded}; curl:{$this->useCurl})"); + + return $meta; + } + + /** + * Upload a file to dropbox + * + * @access public + * @param $src_file string Local file to upload + * @param $dropbox_path string Dropbox path for destination + * @return object Dropbox file metadata + */ + public function UploadFile($src_file, $dropbox_path='', $overwrite=true, $parent_rev=null) + { + if(empty($dropbox_path)) $dropbox_path = basename($src_file); + elseif(is_object($dropbox_path) && !empty($dropbox_path->path)) $dropbox_path = $dropbox_path->path; + + // make sure the dropbox_path is not a dir. if it is, append baseneme of $src_file + $dropbox_bn = basename($dropbox_path); + if(strpos($dropbox_bn,'.') === false) { // check if ext. is missing -> could be a directory! + try { + $meta = $this->GetMetadata($dropbox_path); + if($meta && $meta->is_dir) + $dropbox_path = $dropbox_path . '/'. basename($src_file); + } catch(Exception $e) {} + } + + $file_size = filesize($src_file); + + if($file_size > self::MAX_UPLOAD_CHUNK_SIZE) + { + $fh = fopen($src_file,'rb'); + if($fh === false) + throw new DropboxException(); + + $upload_id = null; + $offset = 0; + + + while(!feof($fh)) { + $url = $this->cleanUrl(self::API_CONTENT_URL."/chunked_upload").'?'.http_build_query(compact('upload_id', 'offset'),'','&'); + $content = fread($fh, self::UPLOAD_CHUNK_SIZE); + $context = $this->createRequestContext($url, "PUT", $content); + + if($this->useCurl) { + curl_setopt($context, CURLOPT_BINARYTRANSFER, true); + $response = json_decode(self::execCurlAndClose($context)); + } else { + $response = json_decode(file_get_contents($url, false, $context)); + } + $offset += strlen($content); + unset($content); + unset($context); + + self::checkForError($response); + + if(empty($upload_id)) { + $upload_id = $response->upload_id; + if(empty($upload_id)) throw new DropboxException("Upload ID empty!"); + } + if($offset >= $file_size) + break; + } + + @fclose($fh); + + return $this->apiCall("commit_chunked_upload/$this->rootPath/$dropbox_path", "POST", compact('overwrite','parent_rev','upload_id'), true); + } + + $query = http_build_query(array_merge(compact('overwrite', 'parent_rev'), array('locale' => $this->locale)),'','&'); + $url = $this->cleanUrl(self::API_CONTENT_URL."/files_put/$this->rootPath/$dropbox_path")."?$query"; + + if($this->useCurl) { + $context = $this->createRequestContext($url, "PUT"); + curl_setopt($context, CURLOPT_BINARYTRANSFER, true); + $fh = fopen($src_file, 'rb'); + curl_setopt($context, CURLOPT_PUT, 1); + curl_setopt($context, CURLOPT_INFILE, $fh); // file pointer + curl_setopt($context, CURLOPT_INFILESIZE, filesize($src_file)); + $meta = json_decode(self::execCurlAndClose($context)); + fclose($fh); + return self::checkForError($meta); + } else { + $content = file_get_contents($src_file); + if(strlen($content) == 0) + throw new DropboxException("Could not read file $src_file or file is empty!"); + + $context = $this->createRequestContext($url, "PUT", $content); + + return self::checkForError(json_decode(file_get_contents($url, false, $context))); + } + } + + /** + * Get thumbnail for a specified image + * + * @access public + * @param $dropbox_file string Path to the image + * @param $format string Image format of the thumbnail (jpeg or png) + * @param $size string Thumbnail size (xs, s, m, l, xl) + * @return mime/* Returns the thumbnail as binary image data + */ + public function GetThumbnail($dropbox_file, $size = 's', $format = 'jpeg', $echo = false) + { + if(is_object($dropbox_file) && !empty($dropbox_file->path)) $dropbox_file = $dropbox_file->path; + $url = $this->cleanUrl(self::API_CONTENT_URL."thumbnails/$this->rootPath/$dropbox_file") + . '?' . http_build_query(array('format' => $format, 'size' => $size),'','&'); + $context = $this->createRequestContext($url, "GET"); + + if($this->useCurl) { + curl_setopt($context, CURLOPT_BINARYTRANSFER, true); + curl_setopt($context, CURLOPT_RETURNTRANSFER, true); + } + + $thumb = $this->useCurl ? self::execCurlAndClose($context) : file_get_contents($url, NULL, $context); + + if($echo) { + header('Content-type: image/'.$format); + echo $thumb; + unset($thumb); + return; + } + + return $thumb; + } + + + function GetLink($dropbox_file, $preview=true, $short=true, &$expires=null) + { + if(is_object($dropbox_file) && !empty($dropbox_file->path)) $dropbox_file = $dropbox_file->path; + $url = $this->apiCall(($preview?"shares":"media")."/$this->rootPath/$dropbox_file", "POST", array('locale' => null, 'short_url'=> $preview ? $short : null)); + $expires = strtotime($url->expires); + return $url->url; + } + + function Delta($cursor) + { + return $this->apiCall("delta", "POST", compact('cursor')); + } + + function GetRevisions($dropbox_file, $rev_limit=10) + { + if(is_object($dropbox_file) && !empty($dropbox_file->path)) $dropbox_file = $dropbox_file->path; + return $this->apiCall("revisions/$this->rootPath/$dropbox_file", "GET", compact('rev_limit')); + } + + function Restore($dropbox_file, $rev) + { + if(is_object($dropbox_file) && !empty($dropbox_file->path)) $dropbox_file = $dropbox_file->path; + return $this->apiCall("restore/$this->rootPath/$dropbox_file", "POST", compact('rev')); + } + + function Search($path, $query, $file_limit=1000, $include_deleted=false) + { + return $this->apiCall("search/$this->rootPath/$path", "POST", compact('query','file_limit','include_deleted')); + } + + function GetCopyRef($dropbox_file, &$expires=null) + { + if(is_object($dropbox_file) && !empty($dropbox_file->path)) $dropbox_file = $dropbox_file->path; + $ref = $this->apiCall("copy_ref/$this->rootPath/$dropbox_file", "GET", array('locale' => null)); + $expires = strtotime($ref->expires); + return $ref->copy_ref; + } + + + function Copy($from_path, $to_path, $copy_ref=false) + { + if(is_object($from_path) && !empty($from_path->path)) $from_path = $from_path->path; + return $this->apiCall("fileops/copy", "POST", array('root'=> $this->rootPath, ($copy_ref ? 'from_copy_ref' : 'from_path') => $from_path, 'to_path' => $to_path)); + } + + /** + * Creates a new folder in the DropBox + * + * @access public + * @param $path string The path to the new folder to create + * @return object Dropbox folder metadata + */ + function CreateFolder($path) + { + return $this->apiCall("fileops/create_folder", "POST", array('root'=> $this->rootPath, 'path' => $path)); + } + + /** + * Delete file or folder + * + * @access public + * @param $path mixed The path or metadata of the file/folder to be deleted. + * @return object Dropbox metadata of deleted file or folder + */ + function Delete($path) + { + if(is_object($path) && !empty($path->path)) $path = $path->path; + return $this->apiCall("fileops/delete", "POST", array('locale' =>null, 'root'=> $this->rootPath, 'path' => $path)); + } + + function Move($from_path, $to_path) + { + if(is_object($from_path) && !empty($from_path->path)) $from_path = $from_path->path; + return $this->apiCall("fileops/move", "POST", array('root'=> $this->rootPath, 'from_path' => $from_path, 'to_path' => $to_path)); + } + + function getFileTree($path="", $include_deleted = false, $max_depth = 0, $depth=0) + { + static $files; + if($depth == 0) $files = array(); + + $dir = $this->apiCall("metadata/$this->rootPath/$path", "GET", compact('include_deleted')); + + if(empty($dir) || !is_object($dir)) return false; + + if(!empty($dir->error)) throw new DropboxException($dir->error); + + foreach($dir->contents as $item) + { + $files[trim($item->path,'/')] = $item; + if($item->is_dir && $depth < $max_depth) + { + $this->getFileTree($item->path, $include_deleted, $max_depth, $depth+1); + } + } + + return $files; + } + + function createCurl($url, $http_context) + { + $ch = curl_init($url); + + $curl_opts = array( + CURLOPT_HEADER => false, // exclude header from output + //CURLOPT_MUTE => true, // no output! + CURLOPT_RETURNTRANSFER => true, // but return! + CURLOPT_SSL_VERIFYPEER => false, + ); + + $curl_opts[CURLOPT_CUSTOMREQUEST] = $http_context['method']; + + if(!empty($http_context['content'])) { + $curl_opts[CURLOPT_POSTFIELDS] =& $http_context['content']; + if(defined("CURLOPT_POSTFIELDSIZE")) + $curl_opts[CURLOPT_POSTFIELDSIZE] = strlen($http_context['content']); + } + + $curl_opts[CURLOPT_HTTPHEADER] = array_map('trim',explode("\n",$http_context['header'])); + + curl_setopt_array($ch, $curl_opts); + return $ch; + } + + static private $_curlHeadersRef; + static function _curlHeaderCallback($ch, $header) + { + self::$_curlHeadersRef[] = trim($header); + return strlen($header); + } + + static function &execCurlAndClose($ch, &$out_response_headers = null) + { + if(is_array($out_response_headers)) { + self::$_curlHeadersRef =& $out_response_headers; + curl_setopt($ch, CURLOPT_HEADERFUNCTION, array(__CLASS__, '_curlHeaderCallback')); + } + $res = curl_exec($ch); + $err_no = curl_errno($ch); + $err_str = curl_error($ch); + curl_close($ch); + if($err_no || $res === false) { + throw new DropboxException("cURL-Error ($err_no): $err_str"); + } + + return $res; + } + + private function createRequestContext($url, $method, &$content=null, $oauth_token=-1) + { + if($oauth_token === -1) + $oauth_token = $this->accessToken; + + $method = strtoupper($method); + $http_context = array('method'=>$method, 'header'=> ''); + + $oauth = new OAuthSimple($this->consumerToken['t'],$this->consumerToken['s']); + + if(empty($oauth_token) && !empty($this->accessToken)) + $oauth_token = $this->accessToken; + + if(!empty($oauth_token)) { + $oauth->setParameters(array('oauth_token' => $oauth_token['t'])); + $oauth->signatures(array('oauth_secret'=>$oauth_token['s'])); + } + + if(!empty($content)) { + $post_vars = ($method != "PUT" && preg_match("/^[a-z][a-z0-9_]*=/i", substr($content, 0, 32))); + $http_context['header'] .= "Content-Length: ".strlen($content)."\r\n"; + $http_context['header'] .= "Content-Type: application/".($post_vars?"x-www-form-urlencoded":"octet-stream")."\r\n"; + $http_context['content'] =& $content; + if($method == "POST" && $post_vars) + $oauth->setParameters($content); + } elseif($method == "POST") { + // make sure that content-length is always set when post request (otherwise some wrappers fail!) + $http_context['content'] = ""; + $http_context['header'] .= "Content-Length: 0\r\n"; + } + + + // check for query vars in url and add them to oauth parameters (and remove from path) + $path = $url; + $query = strrchr($url,'?'); + if(!empty($query)) { + $oauth->setParameters(substr($query,1)); + $path = substr($url, 0, -strlen($query)); + } + + + $signed = $oauth->sign(array( + 'action' => $method, + 'path'=> $path)); + //print_r($signed); + + $http_context['header'] .= "Authorization: ".$signed['header']."\r\n"; + + return $this->useCurl ? $this->createCurl($url, $http_context) : stream_context_create(array('http'=>$http_context)); + } + + private function authCall($path, $request_token=null) + { + $url = $this->cleanUrl(self::API_URL.$path); + $dummy = null; + $context = $this->createRequestContext($url, "POST", $dummy, $request_token); + + $contents = $this->useCurl ? self::execCurlAndClose($context) : file_get_contents($url, false, $context); + $data = array(); + parse_str($contents, $data); + return $data; + } + + private static function checkForError($resp) + { + if(!empty($resp->error)) + throw new DropboxException($resp->error); + return $resp; + } + + + private function apiCall($path, $method, $params=array(), $content_call=false) + { + $url = $this->cleanUrl(($content_call ? self::API_CONTENT_URL : self::API_URL).$path); + $content = http_build_query(array_merge(array('locale'=>$this->locale), $params),'','&'); + + if($method == "GET") { + $url .= "?".$content; + $content = null; + } + + $context = $this->createRequestContext($url, $method, $content); + $json = $this->useCurl ? self::execCurlAndClose($context) : file_get_contents($url, false, $context); + //if($json === false) +// throw new DropboxException(); + $resp = json_decode($json); + return self::checkForError($resp); + } + + + private static function getMetaFromHeaders(&$header_array, $throw_on_error=false) + { + $obj = json_decode(substr(@array_shift(array_filter($header_array, create_function('$s', 'return stripos($s, "x-dropbox-metadata:") === 0;'))), 20)); + if($throw_on_error && (empty($obj)||!is_object($obj))) + throw new DropboxException("Could not retrieve meta data from header data: ".print_r($header_array,true)); + if($throw_on_error) + self::checkForError ($obj); + return $obj; + } + + + function cleanUrl($url) { + $p = substr($url,0,8); + $url = str_replace('//','/', str_replace('\\','/',substr($url,8))); + $url = rawurlencode($url); + $url = str_replace('%2F', '/', $url); + return $p.$url; + } +} + +class DropboxException extends Exception { + + public function __construct($err = null, $isDebug = FALSE) + { + if(is_null($err)) { + $el = error_get_last(); + $this->message = $el['message']; + $this->file = $el['file']; + $this->line = $el['line']; + } else + $this->message = $err; + self::log_error($err); + if ($isDebug) + { + self::display_error($err, TRUE); + } + } + + public static function log_error($err) + { + error_log($err, 0); + } + + public static function display_error($err, $kill = FALSE) + { + print_r($err); + if ($kill === FALSE) + { + die(); + } + } +} diff --git a/DropBoxPhp/OAuthSimple.php b/DropBoxPhp/OAuthSimple.php new file mode 100644 index 0000000..130123b --- /dev/null +++ b/DropBoxPhp/OAuthSimple.php @@ -0,0 +1,532 @@ + + * @copyright unitedHeroes.net 2011 + * @version 1.3 + * @license See OAuthSimple_license.txt + * + */ + +class OAuthSimple { + private $_secrets; + private $_default_signature_method; + private $_action; + private $_nonce_chars; + + /** + * Constructor + * + * @access public + * @param api_key (String) The API Key (sometimes referred to as the consumer key) This value is usually supplied by the site you wish to use. + * @param shared_secret (String) The shared secret. This value is also usually provided by the site you wish to use. + * @return OAuthSimple (Object) + */ + function __construct ($APIKey = "", $sharedSecret=""){ + + if (!empty($APIKey)) + { + $this->_secrets['consumer_key'] = $APIKey; + } + + if (!empty($sharedSecret)) + { + $this->_secrets['shared_secret'] = $sharedSecret; + } + + $this->_default_signature_method = "HMAC-SHA1"; + $this->_action = "GET"; + $this->_nonce_chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + + return $this; + } + + /** + * Reset the parameters and URL + * + * @access public + * @return OAuthSimple (Object) + */ + public function reset() { + $this->_parameters = Array(); + $this->path = NULL; + $this->sbs = NULL; + + return $this; + } + + /** + * Set the parameters either from a hash or a string + * + * @access public + * @param(string, object) List of parameters for the call, this can either be a URI string (e.g. "foo=bar&gorp=banana" or an object/hash) + * @return OAuthSimple (Object) + */ + public function setParameters ($parameters=Array()) { + + if (is_string($parameters)) + { + $parameters = $this->_parseParameterString($parameters); + } + if (empty($this->_parameters)) + { + $this->_parameters = $parameters; + } + else if (!empty($parameters)) + { + $this->_parameters = array_merge($this->_parameters,$parameters); + } + if (empty($this->_parameters['oauth_nonce'])) + { + $this->_getNonce(); + } + if (empty($this->_parameters['oauth_timestamp'])) + { + $this->_getTimeStamp(); + } + if (empty($this->_parameters['oauth_consumer_key'])) + { + $this->_getApiKey(); + } + if (empty($this->_parameters['oauth_token'])) + { + $this->_getAccessToken(); + } + if (empty($this->_parameters['oauth_signature_method'])) + { + $this->setSignatureMethod(); + } + if (empty($this->_parameters['oauth_version'])) + { + $this->_parameters['oauth_version']="1.0"; + } + + return $this; + } + + /** + * Convenience method for setParameters + * + * @access public + * @see setParameters + */ + public function setQueryString ($parameters) + { + return $this->setParameters($parameters); + } + + /** + * Set the target URL (does not include the parameters) + * + * @param path (String) the fully qualified URI (excluding query arguments) (e.g "http://example.org/foo") + * @return OAuthSimple (Object) + */ + public function setURL ($path) + { + if (empty($path)) + { + throw new OAuthSimpleException('No path specified for OAuthSimple.setURL'); + } + $this->_path=$path; + + return $this; + } + + /** + * Convenience method for setURL + * + * @param path (String) + * @see setURL + */ + public function setPath ($path) + { + return $this->_path=$path; + } + + /** + * Set the "action" for the url, (e.g. GET,POST, DELETE, etc.) + * + * @param action (String) HTTP Action word. + * @return OAuthSimple (Object) + */ + public function setAction ($action) + { + if (empty($action)) + { + $action = 'GET'; + } + $action = strtoupper($action); + if (preg_match('/[^A-Z]/',$action)) + { + throw new OAuthSimpleException('Invalid action specified for OAuthSimple.setAction'); + } + $this->_action = $action; + + return $this; + } + + /** + * Set the signatures (as well as validate the ones you have) + * + * @param signatures (object) object/hash of the token/signature pairs {api_key:, shared_secret:, oauth_token: oauth_secret:} + * @return OAuthSimple (Object) + */ + public function signatures ($signatures) + { + if (!empty($signatures) && !is_array($signatures)) + { + throw new OAuthSimpleException('Must pass dictionary array to OAuthSimple.signatures'); + } + if (!empty($signatures)) + { + if (empty($this->_secrets)) + { + $this->_secrets=Array(); + } + $this->_secrets=array_merge($this->_secrets,$signatures); + } + if (isset($this->_secrets['api_key'])) + { + $this->_secrets['consumer_key'] = $this->_secrets['api_key']; + } + if (isset($this->_secrets['access_token'])) + { + $this->_secrets['oauth_token'] = $this->_secrets['access_token']; + } + if (isset($this->_secrets['access_secret'])) + { + $this->_secrets['oauth_secret'] = $this->_secrets['access_secret']; + } + if (isset($this->_secrets['access_token_secret'])) + { + $this->_secrets['oauth_secret'] = $this->_secrets['access_token_secret']; + } + if (empty($this->_secrets['consumer_key'])) + { + throw new OAuthSimpleException('Missing required consumer_key in OAuthSimple.signatures'); + } + if (empty($this->_secrets['shared_secret'])) + { + throw new OAuthSimpleException('Missing requires shared_secret in OAuthSimple.signatures'); + } + if (!empty($this->_secrets['oauth_token']) && empty($this->_secrets['oauth_secret'])) + { + throw new OAuthSimpleException('Missing oauth_secret for supplied oauth_token in OAuthSimple.signatures'); + } + + return $this; + } + + public function setTokensAndSecrets($signatures) + { + return $this->signatures($signatures); + } + + /** + * Set the signature method (currently only Plaintext or SHA-MAC1) + * + * @param method (String) Method of signing the transaction (only PLAINTEXT and SHA-MAC1 allowed for now) + * @return OAuthSimple (Object) + */ + public function setSignatureMethod ($method="") + { + if (empty($method)) + { + $method = $this->_default_signature_method; + } + $method = strtoupper($method); + switch($method) + { + case 'PLAINTEXT': + case 'HMAC-SHA1': + $this->_parameters['oauth_signature_method']=$method; + break; + default: + throw new OAuthSimpleException ("Unknown signing method $method specified for OAuthSimple.setSignatureMethod"); + break; + } + + return $this; + } + + /** sign the request + * + * note: all arguments are optional, provided you've set them using the + * other helper functions. + * + * @param args (Array) hash of arguments for the call {action, path, parameters (array), method, signatures (array)} all arguments are optional. + * @return (Array) signed values + */ + public function sign($args=array()) + { + if (!empty($args['action'])) + { + $this->setAction($args['action']); + } + if (!empty($args['path'])) + { + $this->setPath($args['path']); + } + if (!empty($args['method'])) + { + $this->setSignatureMethod($args['method']); + } + if (!empty($args['signatures'])) + { + $this->signatures($args['signatures']); + } + if (empty($args['parameters'])) + { + $args['parameters']=array(); + } + $this->setParameters($args['parameters']); + $normParams = $this->_normalizedParameters(); + $this->_parameters['oauth_signature'] = $this->_generateSignature($normParams); + + return Array ( + 'parameters' => $this->_parameters, + 'signature' => self::_oauthEscape($this->_parameters['oauth_signature']), + 'signed_url' => $this->_path . '?' . $this->_normalizedParameters(), + 'header' => $this->getHeaderString(), + 'sbs'=> $this->sbs + ); + } + + /** + * Return a formatted "header" string + * + * NOTE: This doesn't set the "Authorization: " prefix, which is required. + * It's not set because various set header functions prefer different + * ways to do that. + * + * @param args (Array) + * @return $result (String) + */ + public function getHeaderString ($args=array()) + { + if (empty($this->_parameters['oauth_signature'])) + { + $this->sign($args); + } + $result = 'OAuth '; + + foreach ($this->_parameters as $pName => $pValue) + { + if (strpos($pName,'oauth_') !== 0 || $pName == 'oauth_token_secret2') + { + continue; + } + if (is_array($pValue)) + { + foreach ($pValue as $val) + { + $result .= $pName .'="' . self::_oauthEscape($val) . '", '; + } + } + else + { + $result .= $pName . '="' . self::_oauthEscape($pValue) . '", '; + } + } + + return preg_replace('/, $/','',$result); + } + + private function _parseParameterString ($paramString) + { + $elements = explode('&',$paramString); + $result = array(); + foreach ($elements as $element) + { + list ($key,$token) = explode('=',$element); + if ($token) + { + $token = urldecode($token); + } + if (!empty($result[$key])) + { + if (!is_array($result[$key])) + { + $result[$key] = array($result[$key],$token); + } + else + { + array_push($result[$key],$token); + } + } + else + $result[$key]=$token; + } + return $result; + } + + + private static function _oauthEscape($string) + { + if ($string === 0) { return 0; } + if ($string == '0') { return '0'; } + if (strlen($string) == 0) { return ''; } + if (is_array($string)) { + throw new OAuthSimpleException('Array passed to _oauthEscape'); + } + $string = rawurlencode($string); + + $string = str_replace('+','%20',$string); + $string = str_replace('!','%21',$string); + $string = str_replace('*','%2A',$string); + $string = str_replace('\'','%27',$string); + $string = str_replace('(','%28',$string); + $string = str_replace(')','%29',$string); + + return $string; + } + + private function _getNonce($length=5) + { + $result = ''; + $cLength = strlen($this->_nonce_chars); + for ($i=0; $i < $length; $i++) + { + $rnum = rand(0,$cLength); + $result .= substr($this->_nonce_chars,$rnum,1); + } + $result = md5($result); + $this->_parameters['oauth_nonce'] = $result; + + return $result; + } + + private function _getApiKey() + { + if (empty($this->_secrets['consumer_key'])) + { + throw new OAuthSimpleException('No consumer_key set for OAuthSimple'); + } + $this->_parameters['oauth_consumer_key']=$this->_secrets['consumer_key']; + + return $this->_parameters['oauth_consumer_key']; + } + + private function _getAccessToken() + { + if (!isset($this->_secrets['oauth_secret'])) + { + return ''; + } + if (!isset($this->_secrets['oauth_token'])) + { + throw new OAuthSimpleException('No access token (oauth_token) set for OAuthSimple.'); + } + $this->_parameters['oauth_token'] = $this->_secrets['oauth_token']; + + return $this->_parameters['oauth_token']; + } + + private function _getTimeStamp() + { + return $this->_parameters['oauth_timestamp'] = time(); + } + + private function _normalizedParameters() + { + $normalized_keys = array(); + $return_array = array(); + + foreach ( $this->_parameters as $paramName=>$paramValue) { + if (!preg_match('/\w+_secret/',$paramName) OR (strpos($paramValue, '@') !== 0 && !file_exists(substr($paramValue, 1))) ) + { + if (is_array($paramValue)) + { + $normalized_keys[self::_oauthEscape($paramName)] = array(); + foreach($paramValue as $item) + { + array_push($normalized_keys[self::_oauthEscape($paramName)], self::_oauthEscape($item)); + } + } + else + { + $normalized_keys[self::_oauthEscape($paramName)] = self::_oauthEscape($paramValue); + } + } + } + + ksort($normalized_keys); + + foreach($normalized_keys as $key=>$val) + { + if (is_array($val)) + { + sort($val); + foreach($val as $element) + { + array_push($return_array, $key . "=" . $element); + } + } + else + { + array_push($return_array, $key .'='. $val); + } + + } + + return join("&", $return_array); + } + + + private function _generateSignature () + { + $secretKey = ''; + if(isset($this->_secrets['shared_secret'])) + { + $secretKey = self::_oauthEscape($this->_secrets['shared_secret']); + } + + $secretKey .= '&'; + if(isset($this->_secrets['oauth_secret'])) + { + $secretKey .= self::_oauthEscape($this->_secrets['oauth_secret']); + } + + switch($this->_parameters['oauth_signature_method']) + { + case 'PLAINTEXT': + return urlencode($secretKey);; + case 'HMAC-SHA1': + $this->sbs = self::_oauthEscape($this->_action).'&'.self::_oauthEscape($this->_path).'&'.self::_oauthEscape($this->_normalizedParameters()); + + return base64_encode(hash_hmac('sha1',$this->sbs,$secretKey,TRUE)); + default: + throw new OAuthSimpleException('Unknown signature method for OAuthSimple'); + break; + } + } +} + +class OAuthSimpleException extends Exception { + + public function __construct($err, $isDebug = FALSE) + { + self::log_error($err); + if ($isDebug) + { + self::display_error($err, TRUE); + } + } + + public static function log_error($err) + { + error_log($err, 0); + } + + public static function display_error($err, $kill = FALSE) + { + print_r($err); + if ($kill === FALSE) + { + die(); + } + } +} diff --git a/DropBoxPhp/sample-form.php b/DropBoxPhp/sample-form.php new file mode 100644 index 0000000..c439f63 --- /dev/null +++ b/DropBoxPhp/sample-form.php @@ -0,0 +1,85 @@ + "ft0zodv89xx804e", + 'app_secret' => "ut43sn7m9wufy3s", + 'app_full_access' => true, +),'it'); + +handle_dropbox_auth($dropbox); // see below + +// if there is no upload, show the form +if(empty($_FILES['the_upload'])) { +?> +
+

+ + +

+

+
+"; + echo "\r\n\r\nUploading $upload_name:\r\n"; + $meta = $dropbox->UploadFile($_FILES["the_upload"]["tmp_name"], $upload_name); + print_r($meta); + echo "\r\n done!"; + echo ""; +} + + +// ================================================================================ +// store_token, load_token, delete_token are SAMPLE functions! please replace with your own! +function store_token($token, $name) +{ + file_put_contents("tokens/$name.token", serialize($token)); +} + +function load_token($name) +{ + if(!file_exists("tokens/$name.token")) return null; + return @unserialize(@file_get_contents("tokens/$name.token")); +} + +function delete_token($name) +{ + @unlink("tokens/$name.token"); +} +// ================================================================================ + +function handle_dropbox_auth($dropbox) +{ + // first try to load existing access token + $access_token = load_token("access"); + if(!empty($access_token)) { + $dropbox->SetAccessToken($access_token); + } + elseif(!empty($_GET['auth_callback'])) // are we coming from dropbox's auth page? + { + // then load our previosly created request token + $request_token = load_token($_GET['oauth_token']); + if(empty($request_token)) die('Request token not found!'); + + // get & store access token, the request token is not needed anymore + $access_token = $dropbox->GetAccessToken($request_token); + store_token($access_token, "access"); + delete_token($_GET['oauth_token']); + } + + // checks if access token is required + if(!$dropbox->IsAuthorized()) + { + // redirect user to dropbox auth page + $return_url = "http://".$_SERVER['HTTP_HOST'].$_SERVER['SCRIPT_NAME']."?auth_callback=1"; + $auth_url = $dropbox->BuildAuthorizeUrl($return_url); + $request_token = $dropbox->GetRequestToken(); + store_token($request_token, $request_token['t']); + die("Authentication required. Click here."); + } +} \ No newline at end of file diff --git a/DropBoxPhp/sample.php b/DropBoxPhp/sample.php new file mode 100644 index 0000000..393023b --- /dev/null +++ b/DropBoxPhp/sample.php @@ -0,0 +1,133 @@ + + * @copyright Fabian Schlieper 2012 + * @version 1.1 + * @license See license.txt + * + */ + + +// these 2 lines are just to enable error reporting and disable output buffering (don't include this in you application!) +error_reporting(E_ALL); +enable_implicit_flush(); +// -- end of unneeded stuff + +// if there are many files in your Dropbox it can take some time, so disable the max. execution time +set_time_limit(0); + +require_once("DropboxClient.php"); + +// you have to create an app at https://www.dropbox.com/developers/apps and enter details below: +$dropbox = new DropboxClient(array( + 'app_key' => "ft0zodv89xx804e", + 'app_secret' => "ut43sn7m9wufy3s", + 'app_full_access' => true, +),'it'); + + +// first try to load existing access token +$access_token = load_token("access"); +if(!empty($access_token)) { + $dropbox->SetAccessToken($access_token); + echo "loaded access token:"; + print_r($access_token); +} +elseif(!empty($_GET['auth_callback'])) // are we coming from dropbox's auth page? +{ + // then load our previosly created request token + $request_token = load_token($_GET['oauth_token']); + if(empty($request_token)) die('Request token not found!'); + + // get & store access token, the request token is not needed anymore + $access_token = $dropbox->GetAccessToken($request_token); + store_token($access_token, "access"); + delete_token($_GET['oauth_token']); +} + +// checks if access token is required +if(!$dropbox->IsAuthorized()) +{ + // redirect user to dropbox auth page + $return_url = "http://".$_SERVER['HTTP_HOST'].$_SERVER['SCRIPT_NAME']."?auth_callback=1"; + $auth_url = $dropbox->BuildAuthorizeUrl($return_url); + $request_token = $dropbox->GetRequestToken(); + store_token($request_token, $request_token['t']); + die("Authentication required. Click here."); +} + +echo "
";
+echo "Account:\r\n";
+print_r($dropbox->GetAccountInfo());
+
+$files = $dropbox->GetFiles("",false);
+
+echo "\r\n\r\nFiles:\r\n";
+print_r(array_keys($files));
+
+if(!empty($files)) {
+	$file = reset($files);
+	$test_file = "test_download_".basename($file->path);
+	
+	echo "\r\n\r\nMeta data of $file->path:\r\n";
+	print_r($dropbox->GetMetadata($file->path));
+	
+	echo "\r\n\r\nDownloading $file->path:\r\n";
+	print_r($dropbox->DownloadFile($file, $test_file));
+		
+	echo "\r\n\r\nUploading $test_file:\r\n";
+	print_r($dropbox->UploadFile($test_file));
+	echo "\r\n done!";	
+	
+	echo "\r\n\r\nRevisions of $test_file:\r\n";	
+	print_r($dropbox->GetRevisions($test_file));
+}
+	
+echo "\r\n\r\nSearching for JPG files:\r\n";	
+$jpg_files = $dropbox->Search("/", ".jpg", 5);
+if(empty($jpg_files))
+	echo "Nothing found.";
+else {
+	print_r($jpg_files);
+	$jpg_file = reset($jpg_files);
+
+	echo "\r\n\r\nThumbnail of $jpg_file->path:\r\n";	
+	$img_data = base64_encode($dropbox->GetThumbnail($jpg_file->path));
+	echo "\"Generating";
+}
+
+
+function store_token($token, $name)
+{
+	if(!file_put_contents("tokens/$name.token", serialize($token)))
+		die('
Could not store token! Make sure that the directory `tokens` exists and is writable!'); +} + +function load_token($name) +{ + if(!file_exists("tokens/$name.token")) return null; + return @unserialize(@file_get_contents("tokens/$name.token")); +} + +function delete_token($name) +{ + @unlink("tokens/$name.token"); +} + + + + + +function enable_implicit_flush() +{ + @apache_setenv('no-gzip', 1); + @ini_set('zlib.output_compression', 0); + @ini_set('implicit_flush', 1); + for ($i = 0; $i < ob_get_level(); $i++) { ob_end_flush(); } + ob_implicit_flush(1); + echo ""; +} \ No newline at end of file diff --git a/DropBoxPhp/test_download_Apps b/DropBoxPhp/test_download_Apps new file mode 100644 index 0000000..d538cda --- /dev/null +++ b/DropBoxPhp/test_download_Apps @@ -0,0 +1 @@ +{"error": "File not found"} \ No newline at end of file diff --git a/DropBoxPhp/tokens/access.token b/DropBoxPhp/tokens/access.token new file mode 100644 index 0000000..42b3ffc --- /dev/null +++ b/DropBoxPhp/tokens/access.token @@ -0,0 +1 @@ +a:2:{s:1:"t";s:16:"2lfmugdr7rp3yp2q";s:1:"s";s:15:"i3nrl13aduhhufw";} \ No newline at end of file diff --git a/Service_Manut.zip b/Service_Manut.zip new file mode 100644 index 0000000000000000000000000000000000000000..60e7b96a6ae41d5cb5ee33d72459fd0aa43eed62 GIT binary patch literal 82848 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 F`d=UR+adq} literal 0 HcmV?d00001 diff --git a/config.inc.php b/config.inc.php index 100a90c..0342970 100644 --- a/config.inc.php +++ b/config.inc.php @@ -8,3 +8,7 @@ $allowedHost = array( "blog.gruppolapastamadre.it", "management.gruppolapastamadre.it" ); + +$dirRicetteDropBox = "IlLievitario/Images/Ricette"; + +?> \ No newline at end of file diff --git a/image.php b/image.php index 00f4d85..06715df 100644 --- a/image.php +++ b/image.php @@ -1,103 +1,62 @@ -get('/photos/thumbnail/:imageID(/:createImgTag)', function ($imageID, $createImgTag = 0) use ($app) { - $mysqlconnetion = new MysqlClass; - - $retObj = $mysqlconnetion->queryToObject("select type_format, image_thumbnail from immagini where id=" . $imageID, false); - $mysqlconnetion->disconnetti(); - - if($createImgTag) - echo ''; - else - { - $app->contentType($retObj["type_format"]); - echo $retObj['image_thumbnail']; - } -}); - -$app->get('/photos/:imageID(/:createImgTag)', function ($imageID, $createImgTag = 0) use ($app) { - $mysqlconnetion = new MysqlClass; - - $retObj = $mysqlconnetion->queryToObject("select type_format, image from immagini where id=" . $imageID, false); - $mysqlconnetion->disconnetti(); - - if($createImgTag) - echo ''; -}); - -$app->put('/photos/publish/:imageID', function ($imageID) use ($app) { - $mysqlconnetion = new MysqlClass; - - $query = "update immagini set published = 1, published_date = NOW() where ProfiloID = " . $imageID; - $mysqlconnetion->disconnetti(); - $mysqlconnetion->insertRecord($query); - $mysqlconnetion->disconnetti(); -}); - -$app->post('/photos', function () use ($app) { - $idRicette = $app->request()->post('ricetta_id'); - $profileID = $app->request()->post('keyStore'); - $imageFileName = $_FILES['image']["tmp_name"]; - - $layer = ImageWorkshop::initFromPath($imageFileName); - $layer->resizeByLargestSideInPixel(640, true); - - $layer->save(dirname($imageFileName), basename($imageFileName)); - - $imgData = addslashes(file_get_contents($imageFileName)); - - $layer->resizeByLargestSideInPixel(300, true); - - $layer->save(dirname($imageFileName), basename($imageFileName)); - - $ThumbImageData = addslashes(file_get_contents($imageFileName)); - -// istanza della classe - $mysqlconnetion = new MysqlClass; -//$mysqlconneti on->connetti(); - $query = "insert into immagini(id_ricette, type_format, image_thumbnail, image, from_profile_id, uploaded_date) " . - "values(" . $idRicette . ", '" . image_type_to_mime_type($image->image_type) . - "', '" . $ThumbImageData . "', '" . $imgData . "', '" . $profileID . "', NOW())"; - $newID = $mysqlconnetion->insertRecord($query); - $mysqlconnetion->disconnetti(); - - echo $newID; -}); - -$app->put('/photos/:imageID', function ($imageID) use ($app) { - $imageFileName = $_FILES['image']["tmp_name"]; - - $layer = ImageWorkshop::initFromPath($imageFileName); - $layer->resizeByLargestSideInPixel(640, true); - - $layer->save(dirname($imageFileName), basename($imageFileName)); - - $imgData = addslashes(file_get_contents($imageFileName)); - - $layer->resizeByLargestSideInPixel(300, true); - - $layer->save(dirname($imageFileName), basename($imageFileName)); - - $ThumbImageData = addslashes(file_get_contents($imageFileName)); - -// istanza della classe - $mysqlconnetion = new MysqlClass; -//$mysqlconneti on->connetti(); - $query = "update immagini set (type_format = '" . image_type_to_mime_type($image->image_type) . "', " . - "image_thumbnail = '" . $ThumbImageData . "', " . - "image = '" . $imgData . "' where id=" . $imageID; - - $newID = $mysqlconnetion->insertRecord($query); - $mysqlconnetion->disconnetti(); - - return $newID; -}); +get('/photo/thumbnail/:imageID(/:createImgTag)', function ($imageID, $createImgTag = 0) use ($app, $dirRicetteDropBox) { + $mysqlconnetion = new MysqlClass; + + $retObj = $mysqlconnetion->queryToObject("select type_format, id_ricette from immagini where id=" . $imageID, false); + $mysqlconnetion->disconnetti(); + + + $ext = ""; + if ($retObj["type_format"] == "image/jpeg") { + $ext = "jpeg"; + } else if ($retObj["type_format"] == "image/png") { + $ext = "png"; + } + + $folder = $dirRicetteDropBox . "/" . $retObj["id_ricette"]; + + $imageFileName = $imageID . "_thumb_ricetta." . $ext; + + $dropBoxObj = new myDropBox(); + + if ($createImgTag) { + echo ''; + } + else + $app->response->redirect($dropBoxObj->GetLink($folder . "/" . $imageFileName), 303); +}); + +$app->get('/photo/:imageID(/:createImgTag)', function ($imageID, $createImgTag = 0) use ($app, $dirRicetteDropBox) { + $mysqlconnetion = new MysqlClass; + + $retObj = $mysqlconnetion->queryToObject("select type_format, id_ricette from immagini where id=" . $imageID, false); + $mysqlconnetion->disconnetti(); + $ext = ""; + if ($retObj["type_format"] == "image/jpeg") { + $ext = "jpeg"; + } else if ($retObj["type_format"] == "image/png") { + $ext = "png"; + } + + $folder = $dirRicetteDropBox . "/" . $retObj["id_ricette"]; + + $imageFileName = $imageID . "_full_ricetta." . $ext; + + $dropBoxObj = new myDropBox(); + + if ($createImgTag) { + echo ''; + } + else + $app->response->redirect($dropBoxObj->GetLink($folder . "/" . $imageFileName), 303); +}); \ No newline at end of file diff --git a/image_backend.php b/image_backend.php new file mode 100644 index 0000000..87f913a --- /dev/null +++ b/image_backend.php @@ -0,0 +1,111 @@ +get('/photo/publish/:imageID', function ($imageID) use ($app) { + $mysqlconnetion = new MysqlClass; + + $query = "update immagini set published = 1, published_date = NOW() where id = " . $imageID; + + $mysqlconnetion->insertRecord($query); + $mysqlconnetion->disconnetti(); +}); + +$app->delete('/photo/:imageID', function ($imageID) use ($app,$dirRicetteDropBox) { + $mysqlconnetion = new MysqlClass; + + $query = "select id_ricette, type_format from immagini where id = " . $imageID; + $retObj = $mysqlconnetion->queryToObject($query, false); + + $ext = ""; + if ($retObj["type_format"] == "image/jpeg") { + $ext = "jpeg"; + } else if ($retObj["type_format"] == "image/png") { + $ext = "png"; + } + + $dropBoxObj = new myDropBox(); + $folder = $dirRicetteDropBox . "/" . $retObj["id_ricette"]; + $imageFileName = $imageID . "_full_ricetta." . $ext; + $dropBoxObj->Delete($folder . "/" . $imageFileName); + + $imageFileName = $imageID . "_medium_ricetta." . $ext; + $dropBoxObj->Delete($folder . "/" . $imageFileName); + + $imageFileName = $imageID . "_thumb_ricetta." . $ext; + $dropBoxObj->Delete($folder . "/" . $imageFileName); + + $query = "delete from immagini where id = " . $imageID; + $mysqlconnetion->executeQuery($query); + $mysqlconnetion->disconnetti(); +}); + +$app->post('/photos', function () use ($app, $dirRicetteDropBox) { + $idRicette = $app->request()->post('ricetta_id'); + $profileID = $app->request()->post('keyStore'); + $tmpFileName = $_FILES['image']["tmp_name"]; + $layer = ImageWorkshop::initFromPath($tmpFileName); + // istanza della classe + $mysqlconnetion = new MysqlClass; + //$mysqlconneti on->connetti(); + $query = "insert into immagini(id_ricette, type_format, from_profile_id, uploaded_date) " . + "values(" . $idRicette . ", '" . image_type_to_mime_type(exif_imagetype($tmpFileName)) . + "', '" . $profileID . "', NOW())"; + $newID = $mysqlconnetion->insertRecord($query); + $ext = image_type_to_extension(exif_imagetype($tmpFileName), FALSE); + + $folder = $dirRicetteDropBox . "/" . $idRicette; + $imageFileName = $newID . "_full_ricetta." . $ext; + + $dropBoxObj = new myDropBox(); + + resizeImage($dropBoxObj, $layer, 640, dirname($tmpFileName), $imageFileName, $folder); + + $imageMediumFileName = $newID . "_medium_ricetta." . $ext; + + resizeImage($dropBoxObj, $layer, 320, dirname($tmpFileName), $imageMediumFileName, $folder); + + $thumbFileName = $newID . "_thumb_ricetta." . $ext; + + resizeImage($dropBoxObj, $layer, 80, dirname($tmpFileName), $thumbFileName, $folder); + + $mysqlconnetion->disconnetti(); + + echo $newID; +}); + +$app->put('/photos/:imageID', function ($imageID) use ($app, $dirRicetteDropBox) { + $tmpFileName = $_FILES['image']["tmp_name"]; + $layer = ImageWorkshop::initFromPath($tmpFileName); +// istanza della classe + $mysqlconnetion = new MysqlClass; +//$mysqlconneti on->connetti(); + $query = "update immagini set (type_format = '" . image_type_to_mime_type(exif_imagetype($tmpFileName)) . "', " . + " where id=" . $imageID; + + $newID = $mysqlconnetion->insertRecord($query); + + $folder = $dirRicetteDropBox . "/" . $newID; + $imageFileName = $newID . "_full_ricetta." . $ext; + + resizeImage($dropBoxObj, $layer, 640, dirname($tmpFileName), $imageFileName, $folder); + + $imageMediumFileName = $newID . "_medium_ricetta." . $ext; + + resizeImage($dropBoxObj, $layer, 320, dirname($tmpFileName), $imageMediumFileName, $folder); + + $thumbFileName = $newID . "_thumb_ricetta." . $ext; + + resizeImage($dropBoxObj, $layer, 80, dirname($tmpFileName), $thumbFileName, $folder); + + $mysqlconnetion->disconnetti(); + + return $newID; +}); diff --git a/include.php b/include.php index 945b365..dc4072b 100644 --- a/include.php +++ b/include.php @@ -12,8 +12,10 @@ require_once 'Slim/Slim.php'; $app = new \Slim\Slim(); +date_default_timezone_set('Europe/Rome'); + $app->hook('slim.before.router', function () use ($app, $allowedHost) { - $currentRefererRequest = $app->request()->getReferer(); + /*$currentRefererRequest = $app->request()->getReferer(); $currentRefererRequest = substr($currentRefererRequest, 7); //Senza http:// $indexDoublePoint = strpos($currentRefererRequest, ':'); $indexFirstSlash = strpos($currentRefererRequest, '/'); @@ -29,7 +31,7 @@ $app->hook('slim.before.router', function () use ($app, $allowedHost) { { $app->halt(403, "Request arrive from host not allowed " . $currentHostRequest ); return; - } + }*/ if(isset($_SERVER['HTTP_ORIGIN'])) header('Access-Control-Allow-Origin: ' . $_SERVER['HTTP_ORIGIN']); diff --git a/management.php b/management.php index 9a55781..3567410 100644 --- a/management.php +++ b/management.php @@ -30,7 +30,7 @@ $app->get('/typeqtys', function () use ($app) { returnJsonWithDecode($app, $callbackFn, $retObj); }); -$app->get('/categoryitems/:catID', function ($categoryID) use ($app) { +$app->get('/ricette/:catID', function ($categoryID) use ($app) { $callbackFn = $app->request()->get('callback'); $mysqlconnetion = new MysqlClass; //$mysqlconnetion->connetti(); @@ -197,6 +197,18 @@ $app->get('/statistics/themesUsage', function () use ($app) { $query = "SELECT 'TemaUI' as Type, TemaUI as Serie, COUNT(TemaUI) as CountSerie from profilo GROUP BY TemaUI"; + $mysqlconnetion = new MysqlClass; + //$mysqlconnetion->connetti(); + $retObj = $mysqlconnetion->queryToObject($query); + $mysqlconnetion->disconnetti(); + returnJson($app, $callbackFn, $retObj); +}); + +$app->get('/profiles', function () use ($app) { + $callbackFn = $app->request()->get('callback'); + + $query = "SELECT ProfiloID as id, Name as name from profilo where name <> '' order by Name"; + $mysqlconnetion = new MysqlClass; //$mysqlconnetion->connetti(); $retObj = $mysqlconnetion->queryToObject($query); diff --git a/myDropBoxObj.php b/myDropBoxObj.php new file mode 100644 index 0000000..61d8273 --- /dev/null +++ b/myDropBoxObj.php @@ -0,0 +1,83 @@ +dropbox = new DropboxClient( + array( + 'app_key' => "ft0zodv89xx804e", + 'app_secret' => "ut43sn7m9wufy3s", + 'app_full_access' => true + ), 'it'); + $this->internalLoad(); + } + + protected function internalLoad() { + // first try to load existing access token + $access_token = $this->load_token("access"); + if (!empty($access_token)) { + $this->dropbox->SetAccessToken($access_token); + //echo "loaded access token:"; + //print_r($access_token); + } elseif (!empty($_GET['auth_callback'])) { // are we coming from dropbox's auth page? + // then load our previosly created request token + $request_token = $this->load_token($_GET['oauth_token']); + if (empty($request_token)) + die('Request token not found!'); + // get & store access token, the request token is not needed anymore + $access_token = $this->dropbox->GetAccessToken($request_token); + $this->store_token($access_token, "access"); + $this->delete_token($_GET['oauth_token']); + } + // checks if access token is required + if (!$this->dropbox->IsAuthorized()) { + // redirect user to dropbox auth page + $return_url = "http://" . $_SERVER['HTTP_HOST'] . $_SERVER['SCRIPT_NAME'] . "?auth_callback=1"; + $auth_url = $this->dropbox->BuildAuthorizeUrl($return_url); + $request_token = $this->dropbox->GetRequestToken(); + $this->store_token($request_token, $request_token['t']); + die("Authentication required. Click here."); + } + } + + public function UploadFile($fileToUpload, $dropBoxPath) { + $ret = $this->dropbox->UploadFile($fileToUpload, $dropBoxPath); + return true; + } + + public function GetLink($dropBoxPathFile) { + return $this->dropbox->GetLink($dropBoxPathFile, false, false); + } + + public function CreateFolder($dropBoxPath) { + $ret = $this->dropbox->CreateFolder($dropBoxPath); + return true; + } + + public function Delete($dropBoxPath) { + $ret = $this->dropbox->Delete($dropBoxPath); + return true; + } + + private function store_token($token, $name) { + if (!file_put_contents("tokens/$name.token", serialize($token))) + die('
Could not store token! Make sure that the directory `tokens` exists and is writable!'); + } + + private function load_token($name) { + if (!file_exists("tokens/$name.token")) + return null; + return @unserialize(@file_get_contents("tokens/$name.token")); + } + + private function delete_token($name) { + @unlink("tokens/$name.token"); + } + +} diff --git a/nbproject/private/private.properties b/nbproject/private/private.properties new file mode 100644 index 0000000..ee7e44e --- /dev/null +++ b/nbproject/private/private.properties @@ -0,0 +1,6 @@ +copy.src.files=false +copy.src.on.open=false +copy.src.target= +index.file= +run.as=LOCAL +url=http://localhost:8081/Service_Manut/ diff --git a/nbproject/private/private.xml b/nbproject/private/private.xml new file mode 100644 index 0000000..284eeec --- /dev/null +++ b/nbproject/private/private.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/nbproject/project.properties b/nbproject/project.properties new file mode 100644 index 0000000..8b7302c --- /dev/null +++ b/nbproject/project.properties @@ -0,0 +1,7 @@ +include.path=${php.global.include.path} +php.version=PHP_54 +source.encoding=UTF-8 +src.dir=. +tags.asp=false +tags.short=false +web.root=. diff --git a/nbproject/project.xml b/nbproject/project.xml new file mode 100644 index 0000000..ed51825 --- /dev/null +++ b/nbproject/project.xml @@ -0,0 +1,9 @@ + + + org.netbeans.modules.php.project + + + Service_Manut + + + diff --git a/ricette.php b/ricette.php index e63a742..b9b45f4 100644 --- a/ricette.php +++ b/ricette.php @@ -33,11 +33,13 @@ $app->get('/typeqtys', function () use ($app) { returnJson($app, $callbackFn, $retObj); }); -$app->get('/categoryitems/:catID', function ($categoryID) use ($app) { +$app->get('/ricette/:catID', function ($categoryID) use ($app) { $callbackFn = $app->request()->get('callback'); $mysqlconnetion = new MysqlClass; //$mysqlconnetion->connetti(); - $query = "select ID as ricetta_id, titolo, autore, valutazione, difficolta from ricette where ID_CATEGORIA = " . $categoryID . " order by titolo, autore"; + $query = "select * from (select ID as ricetta_id, titolo, autore, valutazione, difficolta, " . + "(select id from immagini where immagini.id_ricette = ricette.id and published = 1 order by published_date limit 1) as firstImage " . + "from ricette where ricette.ID_CATEGORIA = " . $categoryID . " order by titolo, autore) as TMP"; $retObj = $mysqlconnetion->queryToObject($query); $mysqlconnetion->disconnetti(); @@ -48,7 +50,7 @@ $app->get('/categoryitems/:catID', function ($categoryID) use ($app) { returnJson($app, $callbackFn, $retObj); }); -$app->get('/categoryitems/:categoryID/mostvote(/:numItems)', function ($categoryID, $numItems = 10) use ($app) { +$app->get('/ricette/:categoryID/mostvote(/:numItems)', function ($categoryID, $numItems = 10) use ($app) { $callbackFn = $app->request()->get('callback'); $mysqlconnetion = new MysqlClass; //$mysqlconnetion->connetti(); @@ -63,7 +65,7 @@ $app->get('/categoryitems/:categoryID/mostvote(/:numItems)', function ($category returnJson($app, $callbackFn, $retObj); }); -$app->get('/categoryitems/:categoryID/lastinserted(/:numItems)', function ($categoryID, $numItems = 10) use ($app) { +$app->get('/ricette/:categoryID/lastinserted(/:numItems)', function ($categoryID, $numItems = 10) use ($app) { $callbackFn = $app->request()->get('callback'); $mysqlconnetion = new MysqlClass; //$mysqlconnetion->connetti(); @@ -78,7 +80,7 @@ $app->get('/categoryitems/:categoryID/lastinserted(/:numItems)', function ($cate returnJson($app, $callbackFn, $retObj); }); -$app->get('/categoryitems/search/:numItems/:startItem(/:categoryId(/:difficolta(/:titolo)))', +$app->get('/ricette/search/:numItems/:startItem(/:categoryId(/:difficolta(/:titolo)))', function ($numItems = 10, $startItem = 0, $categoryId = 0, $difficolta = 0, $titolo = "") use ($app) { $callbackFn = $app->request()->get('callback'); $mysqlconnetion = new MysqlClass; diff --git a/serviceapp.php b/serviceapp.php index cd1db2e..c5e9a38 100644 --- a/serviceapp.php +++ b/serviceapp.php @@ -2,15 +2,16 @@ include_once "./include.php"; -$app->group('/api', function () use ($app) { +$app->group('/api', function () use ($app, $dirRicetteDropBox) { include "./ricette.php"; include "./profile.php"; include "./image.php"; include "./spacciatoripm.php"; }); -$app->group('/backend', function () use ($app) { +$app->group('/backend', function () use ($app, $dirRicetteDropBox) { include "./management.php"; + include "./image_backend.php"; }); //include "./image.php"; diff --git a/t.php b/t.php new file mode 100644 index 0000000..ee3b988 --- /dev/null +++ b/t.php @@ -0,0 +1,178 @@ +get('/categories', function () use ($app) { + $callbackFn = $app->request()->get('callback'); + $mysqlconnetion = new MysqlClass; + //$mysqlconneti on->connetti(); + $retObj = $mysqlconnetion->queryToObject("select ID as category_id, NAME as name from categorie order by name"); + $mysqlconnetion->disconnetti(); + + returnJson($app, $callbackFn, $retObj); +}); + +$app->get('/typeingredients', function () use ($app) { + $callbackFn = $app->request()->get('callback'); + $mysqlconnetion = new MysqlClass; + //$mysqlconnetion->connetti(); + $retObj = $mysqlconnetion->queryToObject("select ID as tipo_ingredienti_id, name from tipo_ingredienti order by name"); + $mysqlconnetion->disconnetti(); + + returnJson($app, $callbackFn, $retObj); +}); + +$app->get('/typeqtys', function () use ($app) { + $callbackFn = $app->request()->get('callback'); + $mysqlconnetion = new MysqlClass; + //$mysqlconnetion->connetti(); + $retObj = $mysqlconnetion->queryToObject("select ID as tipo_quantita_id, name from tipo_quantita"); + $mysqlconnetion->disconnetti(); + + returnJson($app, $callbackFn, $retObj); +}); + +$app->get('/categoryitems/:catID', function ($categoryID) use ($app) { + $callbackFn = $app->request()->get('callback'); + $mysqlconnetion = new MysqlClass; + //$mysqlconnetion->connetti(); + $query = "select ID as ricetta_id, titolo, autore, valutazione, difficolta from ricette where ID_CATEGORIA = " . $categoryID . " order by titolo, autore"; + $retObj = $mysqlconnetion->queryToObject($query); + $mysqlconnetion->disconnetti(); + + foreach ($retObj as $ele) { + $ele["titolo"] = html_entity_decode($ele["titolo"], ENT_COMPAT | ENT_HTML401, 'ISO-8859-1'); + } + + returnJson($app, $callbackFn, $retObj); +}); + +$app->get('/categoryitems/:categoryID/mostvote(/:numItems)', function ($categoryID, $numItems = 10) use ($app) { + $callbackFn = $app->request()->get('callback'); + $mysqlconnetion = new MysqlClass; + //$mysqlconnetion->connetti(); + $query = "select ID as ricetta_id, titolo, autore from ricette where ID_CATEGORIA = " . $categoryID . " order by Valutazione desc, titolo, autore LIMIT " . $numItems; + $retObj = $mysqlconnetion->queryToObject($query); + $mysqlconnetion->disconnetti(); + + foreach ($retObj as $ele) { + $ele["titolo"] = html_entity_decode($ele["titolo"], ENT_COMPAT | ENT_HTML401, 'ISO-8859-1'); + } + + returnJson($app, $callbackFn, $retObj); +}); + +$app->get('/categoryitems/:categoryID/lastinserted(/:numItems)', function ($categoryID, $numItems = 10) use ($app) { + $callbackFn = $app->request()->get('callback'); + $mysqlconnetion = new MysqlClass; + //$mysqlconnetion->connetti(); + $query = "select ID as ricetta_id, titolo, autore from ricette where ID_CATEGORIA = " . $categoryID . " order by Data_creazione desc, titolo, autore LIMIT " . $numItems; + $retObj = $mysqlconnetion->queryToObject($query); + $mysqlconnetion->disconnetti(); + + foreach ($retObj as $ele) { + $ele["titolo"] = html_entity_decode($ele["titolo"], ENT_COMPAT | ENT_HTML401, 'ISO-8859-1'); + } + + returnJson($app, $callbackFn, $retObj); +}); + +$app->get('/categoryitems/search/:numItems(/:categoryId(/:difficolta(/:titolo)))', + function ($numItems = 10, $categoryId = 0, $difficolta = 0, $titolo = "") use ($app) { + $callbackFn = $app->request()->get('callback'); + //$filterItem = json_decode($app->request()->post('post')); + $mysqlconnetion = new MysqlClass; + $query = "select ricette.ID as ricetta_id, categorie.name AS categoria_name, titolo, autore, valutazione, difficolta from ricette" + . " INNER JOIN categorie ON categorie.ID = ricette.id_categoria" + . " where 1 = 1"; + if ($categoryId > 0) { + $query = $query . " AND ID_CATEGORIA = " . $categoryId; + } + if ($titolo != null && $titolo != "") { + foreach (explode(" ", htmlentities(urldecode($titolo))) as $ele) + $query = $query . " AND titolo like '%" . $ele . "%'"; + } + if ($difficolta > 0) { + $query = $query . " AND difficolta = " . $difficolta; + } + $query = $query . " order by titolo, autore LIMIT " . $numItems; + $retObj = $mysqlconnetion->queryToObject($query); + $mysqlconnetion->disconnetti(); + + foreach ($retObj as $ele) { + $ele["titolo"] = html_entity_decode($ele["titolo"], ENT_COMPAT | ENT_HTML401, 'ISO-8859-1'); + } + returnJson($app, $callbackFn, $retObj); +}); + +$app->get('/ricetta/body/:itemID', function ($itemID) use ($app) { + $callbackFn = $app->request()->get('callback'); + $mysqlconnetion = new MysqlClass; + + $query = "UPDATE ricette Set valutazione = valutazione + 1 WHERE ricette.ID = " . $itemID; + $mysqlconnetion->executeQuery($query); + + $query = "SELECT id_categoria, categorie.name AS categoria_name, ricette.ID AS ricetta_id, titolo, procedimento, autore, link_fonte, link_youtube, valutazione FROM ricette INNER JOIN categorie ON categorie.ID = ricette.id_categoria WHERE ricette.ID = " . $itemID; + $retObj = $mysqlconnetion->queryToObject($query); + + $query2 = "select ingredienti.id_tipo_ingredienti, tipo_ingredienti.name as nome_ingrediente, quantita, ingredienti.id_tipo_quantita, tipo_quantita.Name as unita, note, posizione from ingredienti " . + "inner join tipo_ingredienti on ingredienti.ID_TIPO_INGREDIENTI = tipo_ingredienti.ID " . + "left outer join tipo_quantita on ingredienti.ID_TIPO_QUANTITA = tipo_quantita.ID " . + "where ingredienti.ID_RICETTE = " . $itemID . " order by ingredienti.posizione"; + + $retObj2 = $mysqlconnetion->queryToObject($query2); + + $retObj[0]["titolo"] = html_entity_decode($retObj[0]["titolo"], ENT_COMPAT | ENT_HTML401, 'ISO-8859-1'); + $retObj[0]["procedimento"] = html_entity_decode($retObj[0]["procedimento"], ENT_COMPAT | ENT_HTML401, 'ISO-8859-1'); + $retObj[0]["ingredienti"] = $retObj2; + + $mysqlconnetion->disconnetti(); + + returnJson($app, $callbackFn, $retObj); +}); + +$app->get('/ricetta/header/:itemID', function ($itemID) use ($app) { + $callbackFn = $app->request()->get('callback'); + $mysqlconnetion = new MysqlClass; + $query = "UPDATE ricette Set valutazione = valutazione + 1 WHERE ricette.ID = " . $itemID; + $mysqlconnetion->executeQuery($query); + $query = "SELECT id_categoria, categorie.name AS categoria_name, ricette.ID AS ricetta_id, titolo, procedimento, autore, link_fonte, link_youtube, valutazione FROM ricette INNER JOIN categorie ON categorie.ID = ricette.id_categoria WHERE ricette.ID = " . $itemID; + $retObj = $mysqlconnetion->queryToObject($query); + $retObj[0]["titolo"] = html_entity_decode($retObj[0]["titolo"], ENT_COMPAT | ENT_HTML401, 'ISO-8859-1'); + $retObj[0]["procedimento"] = html_entity_decode($retObj[0]["procedimento"], ENT_COMPAT | ENT_HTML401, 'ISO-8859-1'); + $data = $retObj[0]["link_youtube"]; + $output = array(); + if ($data != "") { + $d = explode(";", $data); + $index = 0; + foreach ($d as $ele) { + $obj["VideoID"] = $ele; + $output[$index] = $obj; + $index++; + } + } + $retObj[0]["link_youtube"] = $output; + $mysqlconnetion->disconnetti(); + returnJson($app, $callbackFn, $retObj); +}); + +$app->get('/ricetta/ingredienti/:itemID', function ($itemID) use ($app) { + $callbackFn = $app->request()->get('callback'); + $mysqlconnetion = new MysqlClass; + $query2 = "select ingredienti.id_tipo_ingredienti, tipo_ingredienti.name as nome_ingrediente, quantita, ingredienti.id_tipo_quantita, tipo_quantita.Name as unita, note, posizione from ingredienti " . + "inner join tipo_ingredienti on ingredienti.ID_TIPO_INGREDIENTI = tipo_ingredienti.ID " . + "left outer join tipo_quantita on ingredienti.ID_TIPO_QUANTITA = tipo_quantita.ID " . + "where ingredienti.ID_RICETTE = " . $itemID . " order by ingredienti.posizione"; + + $retObj2 = $mysqlconnetion->queryToObject($query2); + + //$retObj[0]["titolo"] = html_entity_decode($retObj[0]["titolo"]); + //$retObj[0]["procedimento"] = html_entity_decode($retObj[0]["procedimento"]); + //$retObj[0]["ingredienti"] = $retObj2; + + $mysqlconnetion->disconnetti(); + + returnJson($app, $callbackFn, $retObj2); +}); +?> diff --git a/tokens/access.token b/tokens/access.token new file mode 100644 index 0000000..ba753a0 --- /dev/null +++ b/tokens/access.token @@ -0,0 +1 @@ +a:2:{s:1:"t";s:16:"s1ti76srenx4b7aj";s:1:"s";s:15:"qshrdehad4dzz6n";} \ No newline at end of file diff --git a/utility.php b/utility.php index 76caec5..0e5573f 100644 --- a/utility.php +++ b/utility.php @@ -1,5 +1,14 @@ resizeByLargestSideInPixel($size, true); + $layer->save($dir, $fileName); + + try { + $dropBoxObj->CreateFolder($dirDropBox); + } catch (DropboxException $ex) { + } + + $dropBoxObj->UploadFile($fullPath, $dirDropBox . "/" . $fileName); +} + ?>