From cbed154f2147f041de1d40d5002747b210edda95 Mon Sep 17 00:00:00 2001 From: Oshane Bailey Date: Mon, 22 Dec 2014 13:56:55 -0500 Subject: [PATCH] closed #30 - Added an image uploader and improve vecni backend. --- .gitignore | 1 - .gitmodules | 25 +- .htaccess | 1 + app/controller/user/User.php | 43 +++- app/libs/mysql/MysqlAutoloader.php | 27 +++ app/libs/mysql/PDOConnector.php | 2 +- app/libs/scifile/File.php | 100 ++++++++ app/libs/scifile/Image.php | 33 +++ app/libs/scifile/handler/GDImage.php | 290 +++++++++++++++++++++++ app/libs/scifile/handler/ImageUpload.php | 88 +++++++ app/libs/vecni/Object.php | 106 +-------- app/libs/vecni/ObjectTrait.php | 111 +++++++++ app/libs/vecni/Session.php | 64 +++++ app/libs/vecni/Vecni.php | 76 +++--- app/libs/vecni/http/Request.php | 27 ++- app/libs/vecni/http/Response.php | 23 +- app/libs/vecni/twig/Filter.php | 49 ++++ app/libs/vecni/twig/VecniTwig.php | 31 +++ app/static/ext/jsuploader | 1 + app/static/src/css/less/base.less | 256 +++++++++++++++++--- app/static/src/css/less/carousel.less | 26 ++ app/static/src/css/less/functions.less | 11 + app/static/src/css/less/style.less | 1 + app/static/src/css/less/variables.less | 3 + app/templates/bit/init.html | 0 app/templates/bit/scripts.html | 1 + app/templates/bit/styles.html | 6 +- app/templates/macros/carousel.html | 70 ++++++ main.ini.php | 5 +- 29 files changed, 1265 insertions(+), 212 deletions(-) create mode 100755 app/libs/mysql/MysqlAutoloader.php create mode 100644 app/libs/scifile/File.php create mode 100644 app/libs/scifile/Image.php create mode 100644 app/libs/scifile/handler/GDImage.php create mode 100644 app/libs/scifile/handler/ImageUpload.php create mode 100644 app/libs/vecni/ObjectTrait.php create mode 100644 app/libs/vecni/Session.php create mode 100644 app/libs/vecni/twig/Filter.php create mode 100644 app/libs/vecni/twig/VecniTwig.php create mode 160000 app/static/ext/jsuploader create mode 100644 app/static/src/css/less/carousel.less mode change 100755 => 100644 app/templates/bit/init.html create mode 100644 app/templates/macros/carousel.html diff --git a/.gitignore b/.gitignore index 061b015..9292b0c 100755 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,4 @@ *.DS_Store *.swp .DS_Store -app/configs/settings.ini.php app/static/css/gen/* diff --git a/.gitmodules b/.gitmodules index a462859..a36aa12 100755 --- a/.gitmodules +++ b/.gitmodules @@ -1,15 +1,18 @@ [submodule "app/plugins/twig"] - path = app/plugins/twig - url = git://github.com/fabpot/Twig.git + path = app/plugins/twig + url = git://github.com/fabpot/Twig.git [submodule "app/plugins/mailer"] - path = app/plugins/mailer - url = git@github.com:PHPMailer/PHPMailer.git -[submodule "app/plugins/less"] - path = app/plugins/less - url = git@github.com:leafo/lessphp.git + path = app/plugins/mailer + url = git@github.com:PHPMailer/PHPMailer.git [submodule "app/plugins/error"] - path = app/plugins/error - url = git@github.com:JosephLenton/PHP-Error.git + path = app/plugins/error + url = git@github.com:JosephLenton/PHP-Error.git +[submodule "app/plugins/less"] + path = app/plugins/less + url = git@github.com:leafo/lessphp.git [submodule "app/static/ext/sweetalert"] - path = app/static/ext/sweetalert - url = git@github.com:t4t5/sweetalert.git + path = app/static/ext/sweetalert + url = git@github.com:t4t5/sweetalert.git +[submodule "app/static/ext/jsuploader"] + path = app/static/ext/jsuploader + url = git@github.com:b4oshany/AJAX-Uploader.git diff --git a/.htaccess b/.htaccess index 0a2a52f..aab0acf 100755 --- a/.htaccess +++ b/.htaccess @@ -3,5 +3,6 @@ RewriteEngine On # Redirect requests to index.php RewriteCond %{REQUEST_URI} !=index\.php RewriteCond %{REQUEST_FILENAME} !-l +RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteRule .* index.php?%{QUERY_STRING} [L] diff --git a/app/controller/user/User.php b/app/controller/user/User.php index d19c82f..e5f2a48 100755 --- a/app/controller/user/User.php +++ b/app/controller/user/User.php @@ -2,7 +2,7 @@ namespace controller\user; use libs\vecni\Object; use libs\mysql\PDOConnector; -use libs\vecni\Vecni; +use libs\vecni\Vecni as app; /** * @package user @@ -21,13 +21,38 @@ */ class User extends Object{ - public $user_id; // username of user - public $first_name; // user first name - public $last_name; // user last name - public $email; // user email - public $is_login; // user login status - public $status; // user registration status - public $dob; // user date of birth + public $user_id; // id of user. + public $first_name; // user first name. + public $last_name; // user last name. + public $email; // user email. + public $is_login; // user login status. + public $status; // user registration status. + public $dob; // user date of birth. + public $profile_pic; // user profile picture. + + /** + * Return the full name of the user. + * @return string - full name, i.e. first and last name of the user. + */ + public function get_fullname(){ + return "$this->first_name $this->last_name"; + } + + /** + * Return the url of the user profile picture. + * @return string - url of picture. + */ + public function get_profile_pic(){ + if(!empty($this->profile_pic)){ + return $this->profile_pic; + } + return self::get_default_profile_pic(); + } + + + public static function get_default_profile_pic(){ + return "static/src/img/icons/user.png"; + } /** * Login user with their normal user account @@ -124,7 +149,7 @@ public static function start_session(array $data = null){ */ public static function get_current_user(){ if(isset($_SESSION['uid'])){ - return self::object_cast($_SESSION['uid']); + return self::quick_cast($_SESSION['uid']); }else{ return null; } diff --git a/app/libs/mysql/MysqlAutoloader.php b/app/libs/mysql/MysqlAutoloader.php new file mode 100755 index 0000000..43403f2 --- /dev/null +++ b/app/libs/mysql/MysqlAutoloader.php @@ -0,0 +1,27 @@ +"; + if(file_exists($fileName)){ + require_once $fileName; + break; + }else + $fileName = "..".DIRECTORY_SEPARATOR.$fileName; + } +} +spl_autoload_register('\autoload'); +} +?> \ No newline at end of file diff --git a/app/libs/mysql/PDOConnector.php b/app/libs/mysql/PDOConnector.php index ed8c64a..ceaa328 100755 --- a/app/libs/mysql/PDOConnector.php +++ b/app/libs/mysql/PDOConnector.php @@ -3,7 +3,7 @@ require_once "MysqlAutoloader.php"; class PDOConnector{ //privilige user for the database - protected static $database_user; + public static $database_user; protected static $database_pass; //set the database to use diff --git a/app/libs/scifile/File.php b/app/libs/scifile/File.php new file mode 100644 index 0000000..3e1972f --- /dev/null +++ b/app/libs/scifile/File.php @@ -0,0 +1,100 @@ +data_file = $data_file; + if(!empty($this->data_file)){ + $this->name = $this->getName(); + $this->extension = $this->getExtension(); + } + $this->title = $title; + } + + /** + * Set storage location of images. + * @param string $absolute - absolute path to storage folder. + * @param string $relative - relative path to storage folder base on the app root. + */ + public static function register_location($absolute, $relative=null){ + self::$relative_storage = $relative; + self::$absolute_storage = $absolute; + self::mkdir(self::$absolute_storage); + } + + /** + * Builds a file path with the appropriate directory separator. + * @param string $segments,... unlimited number of path segments + * @return string Path + */ + public static function build_path() { + return join(DIRECTORY_SEPARATOR, func_get_args()); + } + + /** + * Make director recursively, where applicable. + * @param string $dir - Path to make if not exists. It is Default to empty string. + * @return bool - True if the directory has been created or already exists, else + * false. + */ + public static function mkdir($dir=""){ + if(!$dir){ + $dir = self::$absolute_storage; + } + if(!is_dir($dir)){ + return mkdir($dir, 0775, true); + } + return true; + } + + /** + * Get storage location of images. + * @param string $type - type of storage to get, i.e. absolute or relative path. + */ + public static function getStorage($type="absolute"){ + if($type=="absolute") + return self::$absoluteStorage; + return self::$relativeStorage; + } + + /** + * Get the name of the file. + * @return string - name of the file. + */ + public function getName(){ + if(strripos($this->data_file,'/') != false){ + $name = substr($this->data_file, strripos($this->data_file,'/')+1, (strlen($this->data_file) - strripos($this->data_file,'.') + 1)); + }else{ + $name = substr($this->data_file, 0, strripos($this->data_file,'.')); + } + return $name; + } + + /** + * Get the file extension. + * @return string - file extension. + */ + public function getExtension(){ + return strtolower(strrchr($this->data_file, '.')); + } + + /** + * Change from absolute to relative path based on the app root. + * @param string $path - Absolute path of file/folder. + * @return string - Relative path of file/folder. + */ + public static function getRelativeFromAbsolute($path){ + if(!empty(self::$base)){ + return str_replace(self::$base, "", $path); + } + return $path; + } +} + +?> diff --git a/app/libs/scifile/Image.php b/app/libs/scifile/Image.php new file mode 100644 index 0000000..14a594a --- /dev/null +++ b/app/libs/scifile/Image.php @@ -0,0 +1,33 @@ +thumb_url; + return $this->photo_url; + } + + public function get($format="xml"){ + $thumbnail = $this->get_location(true); + $fullsize = $this->get_location(false); + $name = $this->name; + $title = $this->title; + switch($format){ + default: + return "$title"; + } + } +} + +?> diff --git a/app/libs/scifile/handler/GDImage.php b/app/libs/scifile/handler/GDImage.php new file mode 100644 index 0000000..82aff34 --- /dev/null +++ b/app/libs/scifile/handler/GDImage.php @@ -0,0 +1,290 @@ + resizeImage(150, 100, 0); +# $resizeObj -> saveImage('images/cars/large/output.jpg', 100); +# +# +# ========================================================================# + + +class GDImage { + // *** Class variables + private $image; + private $width; + private $height; + private $modImage; + private $extension; + private $name; + private $dir; + + + function __construct($fileName) + { + // *** Open up the file + $this->extension = strtolower(strrchr($fileName, '.')); + $this->name = $this->getImageName($fileName); + $this->image = $this->openImage($fileName); + + // *** Get width and height + $this->width = imagesx($this->image); + $this->height = imagesy($this->image); + } + + ## -------------------------------------------------------- + + private function openImage($file) + { + // *** Get extension + $extension = $this->extension; + + switch($extension) + { + case '.jpg': + case '.jpeg': + $img = @imagecreatefromjpeg($file); + break; + case '.gif': + $img = @imagecreatefromgif($file); + break; + case '.png': + $img = @imagecreatefrompng($file); + break; + default: + $img = false; + break; + } + return $img; + } + + public static function renameImage($fileName, $new_name){ + $extension = strtolower(strrchr($fileName, '.')); + if(strripos($fileName,'/') != false){ + $dir = substr($fileName, strripos($fileName,'/')); + $name = $dir.''.$new_name.''.$extension; + }else{ + $name = $new_name.''.$extension; + } + return $name; + } + + public function getImageName($fileName){ + if(strripos($fileName,'/') != false){ + $name = substr($fileName, strripos($fileName,'/')+1, (strlen($fileName) - strripos($fileName,'.') + 1)); + }else{ + $name = substr($fileName, 0, strripos($fileName,'.')); + } + return $name; + } + + + ## -------------------------------------------------------- + + public function resizeImage($newWidth, $newHeight, $option="auto") + { + // *** Get optimal width and height - based on $option + $optionArray = $this->getDimensions($newWidth, $newHeight, $option); + + $optimalWidth = $optionArray['optimalWidth']; + $optimalHeight = $optionArray['optimalHeight']; + + + // *** Resample - create image canvas of x, y size + $this->modImage = imagecreatetruecolor($optimalWidth, $optimalHeight); + imagecopyresampled($this->modImage, $this->image, 0, 0, 0, 0, $optimalWidth, $optimalHeight, $this->width, $this->height); + + + // *** if option is 'crop', then crop too + if ($option == 'crop') { + $this->crop($optimalWidth, $optimalHeight, $newWidth, $newHeight); + } + } + + ## -------------------------------------------------------- + + private function getDimensions($newWidth, $newHeight, $option) + { + + switch ($option) + { + case 'exact': + $optimalWidth = $newWidth; + $optimalHeight= $newHeight; + break; + case 'portrait': + $optimalWidth = $this->getSizeByFixedHeight($newHeight); + $optimalHeight= $newHeight; + break; + case 'landscape': + $optimalWidth = $newWidth; + $optimalHeight= $this->getSizeByFixedWidth($newWidth); + break; + case 'auto': + $optionArray = $this->getSizeByAuto($newWidth, $newHeight); + $optimalWidth = $optionArray['optimalWidth']; + $optimalHeight = $optionArray['optimalHeight']; + break; + case 'crop': + $optionArray = $this->getOptimalCrop($newWidth, $newHeight); + $optimalWidth = $optionArray['optimalWidth']; + $optimalHeight = $optionArray['optimalHeight']; + break; + } + return array('optimalWidth' => $optimalWidth, 'optimalHeight' => $optimalHeight); + } + + ## -------------------------------------------------------- + + private function getSizeByFixedHeight($newHeight) + { + $ratio = $this->width / $this->height; + $newWidth = $newHeight * $ratio; + return $newWidth; + } + + private function getSizeByFixedWidth($newWidth) + { + $ratio = $this->height / $this->width; + $newHeight = $newWidth * $ratio; + return $newHeight; + } + + private function getSizeByAuto($newWidth, $newHeight) + { + if ($this->height < $this->width) + // *** Image to be resized is wider (landscape) + { + $optimalWidth = $newWidth; + $optimalHeight= $this->getSizeByFixedWidth($newWidth); + } + elseif ($this->height > $this->width) + // *** Image to be resized is taller (portrait) + { + $optimalWidth = $this->getSizeByFixedHeight($newHeight); + $optimalHeight= $newHeight; + } + else + // *** Image to be resizerd is a square + { + if ($newHeight < $newWidth) { + $optimalWidth = $newWidth; + $optimalHeight= $this->getSizeByFixedWidth($newWidth); + } else if ($newHeight > $newWidth) { + $optimalWidth = $this->getSizeByFixedHeight($newHeight); + $optimalHeight= $newHeight; + } else { + // *** Sqaure being resized to a square + $optimalWidth = $newWidth; + $optimalHeight= $newHeight; + } + } + + return array('optimalWidth' => $optimalWidth, 'optimalHeight' => $optimalHeight); + } + + ## -------------------------------------------------------- + + private function getOptimalCrop($newWidth, $newHeight) + { + + $heightRatio = $this->height / $newHeight; + $widthRatio = $this->width / $newWidth; + + if ($heightRatio < $widthRatio) { + $optimalRatio = $heightRatio; + } else { + $optimalRatio = $widthRatio; + } + + $optimalHeight = $this->height / $optimalRatio; + $optimalWidth = $this->width / $optimalRatio; + + return array('optimalWidth' => $optimalWidth, 'optimalHeight' => $optimalHeight); + } + + ## -------------------------------------------------------- + + private function crop($optimalWidth, $optimalHeight, $newWidth, $newHeight) + { + // *** Find center - this will be used for the crop + $cropStartX = ( $optimalWidth / 2) - ( $newWidth /2 ); + $cropStartY = ( $optimalHeight/ 2) - ( $newHeight/2 ); + + $crop = $this->modImage; + //imagedestroy($this->modImage); + + // *** Now crop from center to exact requested size + $this->modImage = imagecreatetruecolor($newWidth , $newHeight); + imagecopyresampled($this->modImage, $crop , 0, 0, $cropStartX, $cropStartY, $newWidth, $newHeight , $newWidth, $newHeight); + } + + ## -------------------------------------------------------- + + public function saveImage($dir = '' , $imageQuality="100") + { + $savePath = $dir.'/'.$this->name.'-resized'.$this->extension; + // *** Get extension + $extension = $this->extension; + + switch($extension) + { + case '.jpg': + case '.jpeg': + if (imagetypes() & IMG_JPG) { + imagejpeg($this->modImage, $savePath, $imageQuality); + } + break; + + case '.gif': + if (imagetypes() & IMG_GIF) { + imagegif($this->modImage, $savePath); + } + break; + + case '.png': + // *** Scale quality from 0-100 to 0-9 + $scaleQuality = round(($imageQuality/100) * 9); + + // *** Invert quality setting as 0 is best, not 9 + $invertScaleQuality = 9 - $scaleQuality; + + if (imagetypes() & IMG_PNG) { + imagepng($this->modImage, $savePath, $invertScaleQuality); + } + break; + + // ... etc + + default: + // *** No extension - No save. + break; + } + + imagedestroy($this->modImage); + return $savePath; + } + + public static function create_thumbnail($img, $dir = '', $width = 200, $height = 200, $resize = 'crop'){ + // *** 1) Initialise / load image + $resizeObj = new self($img); + // *** 2) Resize image (options: exact, portrait, landscape, auto, crop) + $resizeObj -> resizeImage($width, $height, $resize); + // *** 3) Save image + return $resizeObj -> saveImage($dir, 100); + } + + + ## -------------------------------------------------------- +} +?> diff --git a/app/libs/scifile/handler/ImageUpload.php b/app/libs/scifile/handler/ImageUpload.php new file mode 100644 index 0000000..a7464f1 --- /dev/null +++ b/app/libs/scifile/handler/ImageUpload.php @@ -0,0 +1,88 @@ +image = $upload_file; + $this->accept_types = $accept_types; + $this->min_size = $min_size; + $this->max_size = $max_size; + if($rename_to != false){ + $this->image["name"] = GDImage::renameImage($this->image["name"], $rename_to); + } + $this->type = strtolower(strrchr($upload_file['name'], '.')); + } + + /** + * Check if the type of uploaded image is accepted based on its type. + * @return bool - true if type uploaded image type is ok, else false. + */ + private function accept_type(){ + $type = $this->type; + $accept_types = $this->accept_types; + if(in_array($type, $accept_types)){return true;}else{return false;} + } + + /** + * Check if the type of uploaded image is accepted based on its size. + * @return bool - true if type uploaded image size is ok, else false. + */ + private function accept_size(){ + $image = $this->image; + $max = $this->max_size; + $min = $this->min_size; + return ($image["size"] < $max && $image["size"] > $min)? true:false; + } + + /** + * Check if the type of uploaded image has any error. + * @return bool - true if type uploaded image has error, else false. + */ + private function error_check(){ + $image = $this->image; + return ($image["error"] > 0)? true : false; + } + + /** + * Process the uploaded image and create a thumbnail image, where applicable. + * @param string $dir - directory to save the uploaded image to. + * @param bool $create_thumbnail - create a thumbnail image if true. + * @param int $thmb_height - height of the thumbnail image. + * @param int $thmb_width - width of the thumbnail image. + */ + public function process_upload($dir = '', $create_thumbnail = false, $thmb_height = 200, $thmb_width = 200){ + $type_status = $this->accept_type(); + $size_status = $this->accept_size(); + if($size_status && $type_status && File::mkdir($dir)){ + $img = ($dir == '')? ''.$dir.$this->image['name'] : ''.$dir.'/'.$this->image['name']; + if(move_uploaded_file($this->image['tmp_name'], $img)){ + if($create_thumbnail){ + $this->thumbnail = GDImage::create_thumbnail($img, $dir, $thmb_width, $thmb_height); + } + $this->fullsize = $img; + return 'success'; + }else{ + return 'failed'; + } + }else{ + return (!$size_status)? 'invalid size '.$this->accept_size() :'invalid type'; + } + } +} + +?> + diff --git a/app/libs/vecni/Object.php b/app/libs/vecni/Object.php index 88df3f2..fa3b9e2 100755 --- a/app/libs/vecni/Object.php +++ b/app/libs/vecni/Object.php @@ -2,110 +2,6 @@ namespace libs\vecni; class Object{ - /** - * Set a new class variable for the current object - * - * @param string $name, name of the class variable to be set - * @param string $value, value to be set to the new class variable - */ - public function __set($name, $value) { - $this->{$name} = $value; - } - - /** - * Get values of the object - * - * @return mixed, value that is stored in a class variable - */ - public function __get($name) { - if(isset($this->$name)){ - return $this->$name; - } - return null; - } - - - /** As of PHP 5.1.0 */ - public function __isset($name){ - return isset($this->$name); - } - - /** As of PHP 5.1.0 */ - public function __unset($name){ - if(isset($this->$name)) - unset($this->$name); - } - - /** - * Set a new class variable for the current object - * - * @param string $name, name of the class variable to be set - * @param string $value, value to be set to the new class variable - */ - public static function set($name, $value) { - static::$$name = $value; - } - - /** - * Get values of the object - * - * @return mixed, value that is stored in a class variable - */ - public static function get($name) { - if(isset(static::$$name)){ - return static::$$name; - } - return null; - } - - public function to_array(){ - return get_object_vars($this); - } - - /** - * Cast an object to another class, keeping the properties, but changing the methods - * - * @param string $class_name - Class name - * @param array $data - array to be converted to the current scoped object - * @return object - object of the current scope - */ - public static function quick_cast(array $data, $strictness = false){ - $object = new static(); - foreach($data as $key => $value){ - if(!$strictness || property_exists($object, $key)) - $object->$key = $value; - } - return $object; - } - - /** - * Cast an object to another class, keeping the properties, but changing the methods - * - * @param string $class_name - Class name - * @param array $data - array to be converted to the current scoped object - * @return object - object of the current scope - */ - public static function cast($class_name, $object){ - return unserialize(sprintf( - 'O:%d:"%s"%s', - strlen($class_name), - $class_name, - strstr(serialize($object), ':') - )); - } - - /** - * Cast an object to another class, keeping the properties, but changing the methods - * - * @param string $class Class name - * @param object $object - * @return object - */ - public function populate($object, $strictness = false){ - foreach($object as $key => $value){ - if(!$strictness || property_exists($this, $key)) - $this->$key = $value; - } - } + use ObjectTrait; } ?> diff --git a/app/libs/vecni/ObjectTrait.php b/app/libs/vecni/ObjectTrait.php new file mode 100644 index 0000000..3fcdeb0 --- /dev/null +++ b/app/libs/vecni/ObjectTrait.php @@ -0,0 +1,111 @@ +{$name} = $value; + } + + /** + * Get values of the object + * + * @return mixed, value that is stored in a class variable + */ + public function __get($name) { + if(isset($this->$name)){ + return $this->$name; + } + return null; + } + + + /** As of PHP 5.1.0 */ + public function __isset($name){ + return isset($this->$name); + } + + /** As of PHP 5.1.0 */ + public function __unset($name){ + if(isset($this->$name)) + unset($this->$name); + } + + /** + * Set a new class variable for the current object + * + * @param string $name, name of the class variable to be set + * @param string $value, value to be set to the new class variable + */ + public static function setStatic($name, $value) { + static::$$name = $value; + } + + /** + * Get values of the object + * + * @return mixed, value that is stored in a class variable + */ + public static function getStatic($name) { + if(isset(static::$$name)){ + return static::$$name; + } + return null; + } + + public function to_array(){ + return get_object_vars($this); + } + + /** + * Cast an object to another class, keeping the properties, but changing the methods + * + * @param string $class_name - Class name + * @param array $data - array to be converted to the current scoped object + * @return object - object of the current scope + */ + public static function quick_cast(array $data, $strictness = false){ + $object = new static(); + foreach($data as $key => $value){ + if(!$strictness || property_exists($object, $key)) + $object->$key = $value; + } + return $object; + } + + /** + * Cast an object to another class, keeping the properties, but changing the methods + * + * @param string $class_name - Class name + * @param array $data - array to be converted to the current scoped object + * @return object - object of the current scope + */ + public static function cast($class_name, $object){ + return unserialize(sprintf( + 'O:%d:"%s"%s', + strlen($class_name), + $class_name, + strstr(serialize($object), ':') + )); + } + + /** + * Cast an object to another class, keeping the properties, but changing the methods + * + * @param string $class Class name + * @param object $object + * @return object + */ + public function populate($object, $strictness = false){ + foreach($object as $key => $value){ + if(!$strictness || property_exists($this, $key)) + $this->$key = $value; + } + } +} +?> diff --git a/app/libs/vecni/Session.php b/app/libs/vecni/Session.php new file mode 100644 index 0000000..c52f67e --- /dev/null +++ b/app/libs/vecni/Session.php @@ -0,0 +1,64 @@ +array( + "time"=>date("m/d/Y H:i:s"), + "client"=>array( + "ip"=>$_SERVER["REMOTE_ADDR"] + ) + ), + "PREVIOUS"=>$previous + ); + } + + /** + * Set a session variable. + * @param string $key - name of the session variable. + * @param mixed $value - value of the session variable. + */ + public static function set($key, $value){ + $_SESSION[$key] = $value; + } + + /** + * Get a session variable. + * @param string $key - name of the session variable. + * @return mixed - value of the session variable. + */ + public static function get($key, $default=false){ + return (isset($_SESSION[$key]))? $_SESSION[$key]: $default; + } + + /** + * Get the current session data. + * @param string $key - name of the current session varaible. + * @return mixed - value of the current session variable. + */ + public static function current_data($key, $default=null){ + $stock = $_SESSION["APP_SESSION"]["CURRENT"]; + return (!empty($stock[$key]))? $stock[$key]: $default; + } + /** + * Get the previous session data. + * @param string $key - name of the previous session varaible. + * @return mixed - value of the previous session variable. + */ + public static function previous_data($key, $default=null){ + $previous = $_SESSION["APP_SESSION"]["PREVIOUS"]; + return (!empty($previous[$key]))? $previous[$key]: $default; + } +} diff --git a/app/libs/vecni/Vecni.php b/app/libs/vecni/Vecni.php index 3b6b895..58718a5 100755 --- a/app/libs/vecni/Vecni.php +++ b/app/libs/vecni/Vecni.php @@ -3,12 +3,6 @@ require_once "Object.php"; -function staticCall($class, $function, $args = array()){ - if (class_exists($class) && method_exists($class, $function)) - return call_user_func_array(array($class, $function), $args); - return null; -} - class Vecni extends Object { /* @@ -27,7 +21,7 @@ class Vecni extends Object # currently viewed route of the user private static $current_route = ""; - + private static $paths = array( "core"=>"", "index"=>"", @@ -44,23 +38,21 @@ class Vecni extends Object public static $routes_file; public static $mdb; - - public static $host; public static $twig; private $vars = array(); private static $app_route = array(); - + public function __construct($file){ - self::init($file); + self::init($file); } public static function init($file) { self::$paths["hostname"] = $_SERVER["SERVER_NAME"]; self::$paths["host"] = dirname($_SERVER["SCRIPT_NAME"]); - + # get the root folder of the application # absolute address self::$paths["core"] = dirname($file); @@ -78,40 +70,51 @@ public static function init($file) self::$paths["controllers"] = self::$paths["core"].DIRECTORY_SEPARATOR."app".DIRECTORY_SEPARATOR."controller".DIRECTORY_SEPARATOR; self::$paths["configs"] = self::$paths["core"].DIRECTORY_SEPARATOR."app".DIRECTORY_SEPARATOR."configs".DIRECTORY_SEPARATOR; - include_once self::$paths["configs"]."settings.ini.php"; + self::get_settings(); self::twig_loader(); + + Session::start(); } - + + public static function get_settings(){ + include_once self::$paths["configs"]."settings.ini.php"; + } + public static function getPluginsFolder(){ return self::$paths["plugins"]; } - + public static function getTemplatesFolder(){ return self::$paths["templates"]; } - + public static function getLibsFolder(){ return self::$paths["libraries"]; } - + public static function getConfigsFolder(){ return self::$paths["configs"]; } - + public static function getIndexFile(){ return self::$paths["index"]; } - + public static function getRootFolder(){ return self::$paths["core"]; } + public static function getHost(){ + return self::$paths["host"]; + } + public static function getStaticFolder($relative_path=true){ return (($relative_path)? self::$paths["host"]."/" : self::$paths["core"].DIRECTORY_SEPARATOR).self::$paths["static"]; } public static function in_development(){ if($_SERVER["SERVER_NAME"] == "localhost"){ + http\Response::disableCaching(); return true; } return false; @@ -155,10 +158,9 @@ public static function twig_loader(){ ."Twig".DIRECTORY_SEPARATOR."Autoloader.php"; if(file_exists($twig_autoload)){ - require_once $twig_autoload; + twig\VecniTwig::setTwigAutoloader($twig_autoload); + twig\VecniTwig::register(); - # register autloading package twig - \Twig_Autoloader::register(); # get the templates folder and prepare template rendering $loader = new \Twig_Loader_Filesystem(self::$paths["templates"]); self::$twig = new \Twig_Environment($loader, array( @@ -169,8 +171,8 @@ public static function twig_loader(){ self::$twig->addGlobal("host", self::$paths["host"]); self::$twig->addGlobal("static", self::getStaticFolder()); - #allow call to static functions. - self::$twig->addFunction('staticCall', new \Twig_Function_Function('staticCall')); + twig\Filter::register(); + }else{ self::get_submodules("Twig"); } @@ -274,8 +276,7 @@ public static function get_route($app_route=null){ return; } } - echo self::error_route(); - http\Response::abort(); + self::error_route(); }else{ require_once self::$paths["route_file"]; try{ @@ -294,10 +295,29 @@ public static function set_error_route($fn_name){ public static function error_route(){ if(isset(self::$app_route["error404"])){ $app_route = self::$app_route["error404"]; - return $app_route(); + $app_route(); + http\Response::abort(); }else{ - return "404. Not Found"; + self::abort(); + } + } + + /** + * Abort all php process and return HTTP status code 404 to the connected client. + * @param string $message - HTTP Response message to output to the client. + It is default to Not Found. + * @param int $status_code - HTTP Response status code. The status code is + default to 404, which means Not Found. + * @param string $status_text - HTTP Response status text. The status text is + default to Not Found. + */ + public static function abort($message = "Not Found", $status_code=404, $status_text="Not Found"){ + if(!http\Request::is_async()){ + echo self::$twig->render('404.html', + array('message'=>$message) + ); } + http\Response::abort($message, $status_code, $status_text); } public static function index_route(){ diff --git a/app/libs/vecni/http/Request.php b/app/libs/vecni/http/Request.php index 7b52f1e..aadd94c 100755 --- a/app/libs/vecni/http/Request.php +++ b/app/libs/vecni/http/Request.php @@ -2,26 +2,33 @@ namespace libs\vecni\http; class Request{ - public static function init(){ - if(!isset($_SESSION["access_key"])){ - $_SESSION["access_key"] = uniqid('vecni_'); + public static function get_request_log(){ + return $_SESSION["CURRENT_REQUEST"] or null; + } + + public static function add_request_log($key, $value){ + if(!isset($_SESSION["CURRENT_REQUEST"])){ + $_SESSION["CURRENT_REQUEST"] = array(); } + $_SESSION["CURRENT_REQUEST"][$key] = $value; } - - private static function filter_input($filter_type, $var){ - return filter_input($filter_type, $var, FILTER_SANITIZE_SPECIAL_CHARS, FILTER_SANITIZE_ENCODED); + + private static function filter_input($var){ + return filter_var($var, FILTER_SANITIZE_SPECIAL_CHARS, FILTER_SANITIZE_ENCODED); } public static function POST($post_name, $return = false){ - $data = self::filter_input(INPUT_POST, $post_name); - return (!empty($_POST[$post_name]))? $data : $return; + return (!empty($_POST[$post_name]))? self::filter_input($_POST[$post_name]) : $return; } public static function GET($post_name, $return = false){ - $data = self::filter_input(INPUT_GET, $post_name); - return (!empty($_GET[$post_name]))? $data : $return; + return (!empty($_GET[$post_name]))? self::filter_input($_GET[$post_name]) : $return; } + public static function FILE_INPUT($input_name, $return = false){ + return (!empty($_FILES[$input_name]))? $_FILES[$input_name]: $return; + } + public static function set($object, $data){ if(!empty($data)){ $object = $data; diff --git a/app/libs/vecni/http/Response.php b/app/libs/vecni/http/Response.php index c000e42..e933c62 100755 --- a/app/libs/vecni/http/Response.php +++ b/app/libs/vecni/http/Response.php @@ -1,5 +1,6 @@ addFilter($filter); + } + + public static function staticCall($class, $function, $args = array()){ + if (class_exists($class) && method_exists($class, $function)) + return call_user_func_array(array($class, $function), $args); + return null; + } + + public static function relative($string=""){ + return app::getHost()."/$string"; + } + + /** + * Register the default filters for Vecni. + */ + public static function register(){ + /* + # Allow call to static functions. + app::$twig->addFunction(new \Twig_SimpleFunction("static", + function($class, $function, $args=array()){ + return self::staticClass($class, $function, $args); + }));*/ + self::addFilter("relative", array(__CLASS__, "relative")); + } + +} + +?> diff --git a/app/libs/vecni/twig/VecniTwig.php b/app/libs/vecni/twig/VecniTwig.php new file mode 100644 index 0000000..e6782ce --- /dev/null +++ b/app/libs/vecni/twig/VecniTwig.php @@ -0,0 +1,31 @@ + diff --git a/app/static/ext/jsuploader b/app/static/ext/jsuploader new file mode 160000 index 0000000..91e8099 --- /dev/null +++ b/app/static/ext/jsuploader @@ -0,0 +1 @@ +Subproject commit 91e809912c92fd58655a39aafc8c6150765f10b2 diff --git a/app/static/src/css/less/base.less b/app/static/src/css/less/base.less index ebfc5c2..3d015b4 100755 --- a/app/static/src/css/less/base.less +++ b/app/static/src/css/less/base.less @@ -1,3 +1,7 @@ +/** +* @package Base +*/ + body { background-color: #f2f2f2; font-family: segoeui, 'Lato', sans-serif; @@ -8,101 +12,159 @@ body { -webkit-font-smoothing: antialiased; -webkit-overflow-scrolling: touch; } +/* Box Style */ -.full-width{ +.full-width { width: 100%; } -.bottom{ +.item-block{ + display: block; +} + +.width-8 { + width: 80%; +} +.width-7 { + width: 70%; +} +.width-6 { + width: 60%; +} +.width-5 { + width: 50%; +} +.width-4 { + width: 40%; +} +.width-3 { + width: 30%; +} +.width-2 { + width: 20%; +} +/* Button Style */ + +.bottom { position: absolute; bottom: 0; } - -.social-button{ +.social-button { height: 35px; } - -#main-content{ +#main-content { min-height: 500px; margin-top: 80px; } +/* Form Style */ -form{ - &[name="user_invite"]{ - .bootstrap-tagsinput input{ +.inline { + display: inline-block; +} +form { + &[name="user_invite"] { + .bootstrap-tagsinput input { font-size: 18px !important; padding-bottom: 10px !important; padding-top: 5px !important; } - - [type="submit"]{ + [type="submit"] { vertical-align: top !important; } } } - - -.alert{ +.carousel .item img { + width: 100%; + height: inherit; + margin-top: -25%; +} +.alert { position: fixed; z-index: 10000; top: 60px; width: 90%; margin: 0 5%; } - /* Titles */ -h1, h2, h3, h4, h5, h6 { + +h1, +h2, +h3, +h4, +h5, +h6 { font-family: 'Lato', sans-serif; font-weight: 300; color: #333; - h1 { - font-size: 40px; + font-size: 40px; } - h3 { color: #95a5a6; font-weight: 400; } - h4 { color: #95a5a6; font-weight: 400; font-size: 20px; } } - - /* Paragraph & Typographic */ + p { line-height: 28px; margin-bottom: 25px; font-size: 16px; } - .centered { text-align: center; } +/* Default box shadow */ + +.default-box-shadow() { + .box-shadow(0 0 5px, #555); +} +@row-margin =15px; + .row-margin { + margin-top: @row-margin; +margin-bottom: @row-margin; + +} +.row-margin-bottom { + margin-bottom: @row-margin; + +} +.row-margin-top { + margin-top: @row-margin; + +} +.row-margin-2x-bottom { + margin-bottom: @row-margin * 2; +} +.row-margin-2x-top { + margin-top: @row-margin * 2; +} +.no-margin { + margin: 0; +} /* Links */ + a { color: #3498db; word-wrap: break-word; .transition(#3498db 0.1s ease-in background 0.1s ease-in); - - &:before, &:after { + &: before, &: after { .transition(#3498db 0.1s ease-in background 0.1s ease-in); } - - &:hover, &:focus { + &:hover, + &:focus { color: #7b7b7b; text-decoration: none; outline: 0; } } - - - hr { +hr { display: block; height: 1px; border: 0; @@ -110,15 +172,137 @@ a { margin: 1em 0; padding: 0; } -.modal-body{ +.modal-body { margin-left: 15px; margin-right: 15px; } - /* Helpers */ -.noCurve{ - border-radius: 0px; - -webkit-border-radius: 0px; - -moz-border-radius: 0px; +.noCurve { + border-radius: 0px; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; +} + +.no-outline{ + .no-outline(); +} + +/* Card Style */ + +.card { + .box-shadow(@main-shadow-thickness, @main-shadow-color); + margin-bottom: 30px; + background: #fff; + .card-content{ + padding: 10px; + } + .card-menu { + border-bottom: 1px solid #ccc; + padding: 10px; + a[rel=tooltip] { + width: 23%; + display: inline-block; + text-align: center; + .glyphicon { + font-size: 28px; + color: #555; + text-shadow: 1px 1px 1px black; + } + } + & ul { + list-style-type: none; + & li { + float: left; + margin-right: 15px; + & a { + margin-right: 18px; + opacity: 0.6; + filter: alpha(opacity=60); + /* For IE8 and earlier */ + transition: opacity 0.5s; + -moz-transition: opacity 0.5s; + -o-transition: opacity 0.5s; + -webkit-transition: opacity 0.5s; + &: hover { + opacity: 1; + filter: alpha(opacity=100); + /* For IE8 and earlier */ + } + } + } + } + } + li.list-group-item { + min-height: 40px; + } +} +.image-card { + border: 1px solid #fafafa; + width: 225px; + height: 210px; + margin: 20px auto; +} +.image-card img { + width: 100%; + height: 100%; + display: block; +} +.count-card { + height: 30px; + margin-bottom: 5px; + border-bottom: 1px solid #ccc; +} +.count-card ul { + list-style-type: none; +} +.count-card ul li { + float: left; + margin-left: -5px; + margin-right: 20px; +} +.tags-card { + width: 225px; + margin: 5px auto; +} +.tags-card a { + text-decoration: none; +} +.tags-card a:hover { + color: #555; + text-decoration: underline; +} +/* End of Card Style */ + + +/* Style buttons */ + +.btn{ + &.btn-transparent{ + border: none; + outline: none; + background: none; + } + + &.btn-transparent:hover{ + color: blue; + } +} + +/* End button styling. */ + +/* Style separator */ +.separator-top{ + border-top: 1px solid @border-color; + padding-top: 5px; +} +.separator-botoom{ + border-botoom: 1px solid @border-color; + padding-botoom: 5px; +} + +.fixed-right{ + position: absolute; + right: 0; + padding: inherit; } diff --git a/app/static/src/css/less/carousel.less b/app/static/src/css/less/carousel.less new file mode 100644 index 0000000..66a9d6a --- /dev/null +++ b/app/static/src/css/less/carousel.less @@ -0,0 +1,26 @@ +/** +* @package Carousel +*/ + +@carousel-height: 400px; +.carousel{ + + .carousel-inner{ + height: @carousel-height; + width: 100%; + + .item{ + height: @carousel-height !important; + + &.active{ + height: @carousel-height !important; + } + } + } + #carousel-settings{ + position: absolute; + right: 5px; + top: 5px; + z-index: 10; + } +} diff --git a/app/static/src/css/less/functions.less b/app/static/src/css/less/functions.less index accaf19..d30d728 100755 --- a/app/static/src/css/less/functions.less +++ b/app/static/src/css/less/functions.less @@ -19,3 +19,14 @@ -o-background-size: @style; background-size: @style; } + +.no-outline(){ + border: none !important; + outline: none !important; + .box-shadow(0 0 0, black); + + &:focus{ + border: none !important; + .box-shadow(0 0 1px,black); + } +} diff --git a/app/static/src/css/less/style.less b/app/static/src/css/less/style.less index a6a182d..194b4be 100755 --- a/app/static/src/css/less/style.less +++ b/app/static/src/css/less/style.less @@ -1,6 +1,7 @@ @import "variables.less"; @import "functions.less"; @import "base.less"; +@import "carousel.less"; @import "header.less"; @import "footer.less"; @import "content.less"; diff --git a/app/static/src/css/less/variables.less b/app/static/src/css/less/variables.less index 633217d..1dec1c8 100755 --- a/app/static/src/css/less/variables.less +++ b/app/static/src/css/less/variables.less @@ -6,3 +6,6 @@ Enter variables that are used globally within the system @modal-header-color: #f5f5f5; @nav-header-color: #3c8dbc; @blend-white: #f9f9f9; +@main-shadow-thickness: 0 0 1px 1px; +@main-shadow-color: #ccc; +@border-color: #fafafa; diff --git a/app/templates/bit/init.html b/app/templates/bit/init.html old mode 100755 new mode 100644 diff --git a/app/templates/bit/scripts.html b/app/templates/bit/scripts.html index 6565f43..e256e09 100755 --- a/app/templates/bit/scripts.html +++ b/app/templates/bit/scripts.html @@ -1,4 +1,5 @@ + diff --git a/app/templates/bit/styles.html b/app/templates/bit/styles.html index 981535e..921118c 100755 --- a/app/templates/bit/styles.html +++ b/app/templates/bit/styles.html @@ -1,7 +1,5 @@ - - - + + - diff --git a/app/templates/macros/carousel.html b/app/templates/macros/carousel.html new file mode 100644 index 0000000..545cba7 --- /dev/null +++ b/app/templates/macros/carousel.html @@ -0,0 +1,70 @@ + +{% macro property(carousel_title, property, indicator=true, modal='') %} + +{% endmacro %} diff --git a/main.ini.php b/main.ini.php index d786a30..f068d06 100755 --- a/main.ini.php +++ b/main.ini.php @@ -7,6 +7,7 @@ User::start_session(); +$less = app::use_less(); Response::init(); // Set the default title of website. @@ -71,9 +72,9 @@ function process_login(){ } }else{ if($status){ - app::nav_back(); + return app::nav_back(); }else{ - signin_require(); + return signin_require(); } } }