dom_array as $key => $node){
if( !is_array($node) || !array_key_exists('tag',$node) ){
continue;
}
$tag = $node['tag'];
if( $tag != 'img' || !isset($node['attributes']['src']) ){
continue;
}
$resized_data = self::ResizedImage($node['attributes']);
if( $resized_data !== false ){
$img = $resized_data['img'];
$index = $resized_data['index'];
$resized_src = \gp\tool::GetDir('/include/image.php',true).'?i='.$index.'&w='.$resized_data['w'].'&h='.$resized_data['h'].'&img='.rawurlencode($img);
$gp_html_output->dom_array[$key]['attributes']['src'] = $resized_src;
$img_list[$index][] = $resized_data['w'].'x'.$resized_data['h'];
$img_list[$index] = array_unique($img_list[$index]);
\gp_resized::$index[$index] = $img;
}
}
$gp_html_output->Rebuild();
$html_content = $gp_html_output->result;
self::ResizedImageUse($orig_list,$img_list);
\gp_resized::SaveIndex();
}
/**
* Attempt to create a resized image
*
*/
public static function ResizedImage($attributes){
global $dataDir, $dirPrefix;
//height and width from style
$css_w = null;
$css_h = null;
if( !empty($attributes['style']) ){
$css_args = explode(';',$attributes['style']);
foreach($css_args as $css_arg){
$css_arg = explode(':',$css_arg);
if( count($css_arg) != 2 ){
continue;
}
$css_key = strtolower(trim($css_arg[0]));
$css_value = strtolower(trim($css_arg[1]));
$px_pos = strpos($css_value,'px');
if( !$px_pos ){
continue;
}
if( $css_key == 'width' ){
$css_w = substr($css_value,0,$px_pos);
}elseif( $css_key == 'height' ){
$css_h = substr($css_value,0,$px_pos);
}
}
}
//width attribute
if( !$css_w && isset($attributes['width']) && is_numeric($attributes['width']) ){
$css_w = $attributes['width'];
}
//height attribute
if( !$css_h && isset($attributes['height']) && is_numeric($attributes['height']) ){
$css_h = $attributes['height'];
}
if( !$css_w || !$css_h ){
return false;
}
//check src
if( empty($attributes['src']) ){
return false;
}
$src = urldecode($attributes['src']);
$img_dir = $dirPrefix.'/data/_uploaded';
if( $src[0] != '/' && strpos($src,$img_dir) !== 0 ){
return false;
}
$src_relative = substr($src,strlen($img_dir));
return self::CreateImage($src_relative,$css_w,$css_h);
}
/**
* Create a resized image of the file at $src_relative
*
*/
public static function CreateImage($src_relative, $width, $height){
global $dataDir;
$src_path = $dataDir . '/data/_uploaded' . $src_relative;
if( !file_exists($src_path) ){
return false;
}
//compare to actual size
$src_img = \gp\tool\Image::getSrcImg($src_path);
if( $src_img === false ){
return false;
}
//Original Size
$actual_w = imagesx($src_img);
$actual_h = imagesy($src_img);
if( $actual_w <= $width && $actual_h <= $height ){
return false;
}
$info = \gp_resized::ImageInfo($src_relative, $width, $height);
if( !$info ){
return false;
}
$dest_index = $info['index'];
if( !$dest_index ){
$dest_index = \gp_resized::NewIndex();
}
$dest_path = $dataDir . '/data/_resized/' . $dest_index . '/' . $info['name'];
$exists_before = file_exists($dest_path);
//make sure the folder exists
if( !\gp\tool\Files::CheckDir( \gp\tool::DirName($dest_path) ) ){
return false;
}
//create new resized image
if( !\gp\tool\Image::createImg($src_img, $dest_path, 0, 0, 0, 0, $width, $height, $actual_w, $actual_h, false, false, $src_path) ){
return false;
}
//not needed if the resized image is larger than the original
if( filesize($dest_path) > filesize($src_path) ){
if( !$exists_before ){
unlink($dest_path);
}
return false;
}
$data = array();
$data['index'] = $dest_index;
$data['w'] = $width;
$data['h'] = $height;
$data['img'] = $src_relative;
return $data;
}
/**
* Record where reduced images are being used so that we can delete them later if they are no longer referenced
* ... no guarantee the reduced image won't be copy & pasted into other pages.. page copies would need to track the data as well
*
*/
public static function ResizedImageUse($list_before,$list_after){
global $dataDir;
//subtract uses no longer
$subtract_use = self::UseDiff($list_before, $list_after);
//add uses
$add_use = self::UseDiff($list_after, $list_before);
//save info for each image
$all_imgs = array_keys($subtract_use + $add_use);
foreach($all_imgs as $index){
$edited = false;
$usage = \gp_resized::GetUsage($index);
//add uses
if( isset($add_use[$index]) && count($add_use[$index]) ){
$edited = true;
foreach($add_use[$index] as $size){
if( isset($usage[$size]) ){
$usage[$size]['uses']++;
}else{
$usage[$size]['uses'] = 1;
$usage[$size]['created'] = time();
}
$usage[$size]['touched'] = time();
}
}
//remove uses
if( isset($subtract_use[$index]) && is_array($subtract_use[$index]) ){
$edited = true;
foreach($subtract_use[$index] as $size){
if( isset($usage[$size]) ){
$usage[$size]['uses']--;
}else{
$usage[$size]['uses'] = 0;
$usage[$size]['created'] = time();//shouldn't happen
}
$usage[$size]['uses'] = max($usage[$size]['uses'],0);
if( $usage[$size]['uses'] == 0 ){
self::DeleteUnused($index, $size);
}
$usage[$size]['touched'] = time();
}
}
//order usage by sizes: small to large
uksort($usage,array('\\gp\\tool\\Editing', 'SizeCompare'));
if( $edited ){
\gp_resized::SaveUsage($index,$usage);
}
}
}
/**
* Delete unused images
* if uses < 1, delete the file, but not the record
*
*/
private static function DeleteUnused($index, $size){
global $dataDir;
list($width,$height) = explode('x',$size);
//make sure the image still exists
if( !isset(\gp_resized::$index[$index]) ){
return;
}
$img = \gp_resized::$index[$index];
$info = \gp_resized::ImageInfo($img,$width,$height);
if( !$info ){
return;
}
$full_path = $dataDir.'/data/_resized/'.$index.'/'.$info['name'];
if( file_exists($full_path) ){
@unlink($full_path);
}
}
/**
* Get the use difference
*
*/
private static function UseDiff( $a, $b){
$diff = $a;
foreach($a as $index => $sizes){
if( isset($b[$index]) ){
$diff[$index] = array_diff($a[$index],$b[$index]);
}
}
return $diff;
}
/**
* Replace resized images with their originals
*
*/
public static function RestoreImages($html_content,$img_list){
global $dirPrefix;
includeFile('image.php');
\gp_resized::SetIndex();
//
$images = array();
foreach($img_list as $index => $sizes){
if( !isset(\gp_resized::$index[$index]) ){
continue;
}
$img = \gp_resized::$index[$index];
$original_path = $dirPrefix.'/data/_uploaded'.$img;
foreach($sizes as $size){
list($width,$height) = explode('x',$size);
$resized_path = \gp\tool::GetDir('/include/image.php',true).'?i='.$index.'&w='.$width.'&h='.$height; //not searching for the whole path in case the image was renamed
$images[$resized_path] = $original_path;
}
}
//resize images
$gp_html_output = new \gp\tool\Editing\HTML($html_content);
foreach($gp_html_output->dom_array as $key => $node){
if( !is_array($node) || !array_key_exists('tag',$node) ){
continue;
}
$tag = $node['tag'];
if( $tag != 'img' || !isset($node['attributes']['src']) ){
continue;
}
$src = $node['attributes']['src'];
foreach($images as $resized => $original){
if( strpos($src,$resized) === 0 ){
$gp_html_output->dom_array[$key]['attributes']['src'] = $original;
}
}
}
$gp_html_output->Rebuild();
return $gp_html_output->result;
}
/**
* Comare the sizes of two images
*
*/
public static function SizeCompare($size1, $size2){
list($w1,$h1) = explode('x',$size1);
list($w2,$h2) = explode('x',$size2);
return ($w1*$h1) > ($w2*$h2);
}
/**
* Clean a string that may be used as an internal file path
*
* @param string $path The string to be cleansed
* @return string The cleansed string
*/
public static function CleanArg($path){
$path = self::Sanitize($path);
//all forward slashes
$path = str_replace('\\','/',$path);
//remove directory style changes
$path = str_replace(array('../','./','..'),array('','',''),$path);
//change other characters to underscore
$pattern = '#\\||\\:|\\?|\\*|"|<|>|[[:cntrl:]]#u';
$path = preg_replace( $pattern, '_', $path ) ;
//reduce multiple slashes to single
$pattern = '#\/+#';
$path = preg_replace( $pattern, '/', $path ) ;
return $path;
}
/**
* Clean a string for use as a page title (url)
* Removes potentially problematic characters
*
* @param string $title The string to be cleansed
* @param string $spaces The string spaces will be replaced with
* @return string The cleansed string
*/
public static function CleanTitle($title,$spaces = '_'){
$title = self::Sanitize($title);
if( empty($title) ){
return '';
}
$title = str_replace(array('"',"'",'?','*',':'),array(''),$title); // # needed for entities
$title = str_replace(array('<','>','|','\\'),array(' ',' ',' ','/'),$title);
$title = preg_replace('#\.+([\\\\/])#','$1',$title);
$title = trim($title,'/');
$title = trim($title);
if( $spaces ){
$title = preg_replace( '#[[:space:]]#', $spaces, $title );
}
return $title;
}
/**
* Remove null and control characters from the string
* @return string
*/
public static function Sanitize($string){
$string = \gp\tool\Files::NoNull($string);
// Remove control characters [\x00-\x1F\x7F]
$clean = '';
preg_match_all( '#[^[:cntrl:]]+#u', $string, $matches);
foreach($matches[0] as $match){
$clean .= $match;
}
$clean = rawurldecode($clean); // remove percent encoded strings like %2e%2e%2f
//recursively sanitize
if( strlen($clean) !== strlen($string) ){
$clean = self::Sanitize($clean);
}
return $clean;
}
/**
* Use HTML Tidy to validate the $text
* Only runs when $config['HTML_Tidy'] is off
*
* @param string $text The html content to be checked. Passed by reference
*/
public static function tidyFix(&$text,$ignore_config = false){
global $config;
if( !$ignore_config ){
if( empty($config['HTML_Tidy']) || $config['HTML_Tidy'] == 'off' ){
return true;
}
}
if( !function_exists('tidy_parse_string') ){
return false;
}
$options = array();
$options['wrap'] = 0; //keeps tidy from wrapping... want the least amount of space changing as possible.. could get rid of spaces between words with the str_replaces below
$options['doctype'] = 'omit'; //omit, auto, strict, transitional, user
$options['drop-empty-paras'] = true; //drop empty paragraphs
$options['output-xhtml'] = true; //need this so that
will be
.. etc
$options['show-body-only'] = true;
$options['hide-comments'] = false;
$tidy = tidy_parse_string($text,$options,'utf8');
tidy_clean_repair($tidy);
if( tidy_get_status($tidy) === 2){
// 2 is magic number for fatal error
// http://www.php.net/manual/en/function.tidy-get-status.php
return false;
}
$text = tidy_get_output($tidy);
return true;
}
/**
* Return javascript code to be used with autocomplete (jquery ui)
*
*/
public static function AutoCompleteValues($GetUrl=true,$options = array()){
global $gp_index;
$options += array( 'admin_vals' => true,
'var_name' => 'gptitles'
);
//internal link array
$array = array();
foreach($gp_index as $slug => $id){
$label = \gp\tool::GetLabel($slug);
$label = str_replace( array('<','>','"',''','&'), array('<','>','"',"'",'&') , $label);
if( $GetUrl ){
$slug = \gp\tool::GetUrl($slug,'',false);
$slug = rawurldecode($slug);
}
$array[] = array($label,$slug);
}
if( $options['admin_vals'] && class_exists('admin_tools') ){
$scripts = \gp\admin\Tools::AdminScripts();
foreach($scripts as $url => $info){
if( !isset($info['label']) ){
continue;
}
if( $GetUrl ){
$url = \gp\tool::GetUrl($url,'',false);
$url = rawurldecode($url);
}
$array[] = array($info['label'],$url);
}
}
$code = json_encode($array);
if( $options['var_name'] ){
$code = 'var '.$options['var_name'].' = '.$code.';';
}
return $code;
}
public static function PrepAutoComplete(){
global $page;
\gp\tool::LoadComponents('autocomplete');
$page->head_js[] = '/include/js/autocomplete.js';
}
/**
* Use ckeditor for to edit content
*
* configuration options
* - http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.config.html
*/
public static function UseCK($contents,$name='gpcontent',$options=array()){
global $page, $dataDir;
$options += array('rows'=>'20','cols'=>'50');
echo "\n\n";
echo '
';
$page->head .= "\n".'';
$page->head .= "\n".'';
\gp\tool::LoadComponents('autocomplete');
$page->head_script .= self::AutoCompleteValues(true);
ob_start();
echo "\n\n";
// extra plugins
$config = self::CKConfig( $options, 'json', $plugins );
foreach($plugins as $plugin => $plugin_path){
echo 'CKEDITOR.plugins.addExternal('.json_encode($plugin).','.json_encode($plugin_path).');';
echo "\n";
}
echo '$(".CKEDITAREA").each(function(){';
echo 'CKEDITOR.replace( this, '.$config.' );';
echo 'CKEDITOR.on("instanceReady", function(evt){';
/* echo 'console.log("triggered editor:loaded event with ", { section: evt.editor.element["$"], section_type: "other", label: evt.editor.name });'; */
echo '$(document).trigger("editor:loaded", { editor: evt.editor, section: evt.editor.element["$"], section_type: "other", label: evt.editor.name });';
echo '});';
echo '});';
echo "\n\n";
$page->jQueryCode .= ob_get_clean();
}
public static function CKAdminConfig(){
static $cke_config;
//get ckeditor configuration set by users
if( !is_array($cke_config) ){
$cke_config = \gp\tool\Files::Get('_ckeditor/config','cke_config');
if( !$cke_config ){
$cke_config = array();
}
$cke_config += array('plugins'=>array(),'custom_config'=>array());
}
return $cke_config;
}
/**
* CKEditor configuration settings
* Any settings here take precedence over settings in configuration files defined by the customConfig setting
* Configuration precedence: (1) User (2) Addon (3) $options (4) CMS
*
*/
public static function CKConfig( $options = array(), $config_name = 'config', &$plugins = array() ){
global $config;
$plugins = array();
// 4) CMS defaults
$defaults = array(
//'customConfig' => \gp\tool::GetDir('/include/js/ckeditor_config.js'),
'skin' => 'moono-lisa',
'browser' => true, //not actually a ckeditor configuration value, but we're keeping it now for reverse compat
'smiley_path' => \gp\tool::GetDir('/include/thirdparty/ckeditor/plugins/smiley/images/'),
'height' => 300,
'contentsCss' => \gp\tool::GetDir('/include/css/ckeditor_contents.css'),
'fontSize_sizes' => 'Smaller/smaller;Normal/;Larger/larger;8/8px;9/9px;10/10px;11/11px;12/12px;14/14px;16/16px;18/18px;20/20px;22/22px;24/24px;26/26px;28/28px;36/36px;48/48px;72/72px',
'ignoreEmptyParagraph' => true,
'entities_latin' => false,
'entities_greek' => false,
'scayt_autoStartup' => false,
'disableNativeSpellChecker' => false,
'FillEmptyBlocks' => false,
'autoParagraph' => false,
'versionCheck' => false,
'extraAllowedContent' => 'iframe[align,frameborder,height,longdesc,marginheight,marginwidth,name,sandbox,scrolling,seamless,src,srcdoc,width];script[async,charset,defer,src,type,xml]; *[accesskey,contenteditable,contextmenu,dir,draggable,dropzone,hidden,id,lang,spellcheck,style,tabindex,title,translate](*)',
'toolbar' => array(
array('Sourcedialog','Templates','ShowBlocks','Undo','Redo','RemoveFormat'), //,'Maximize' does not work well
array('Cut','Copy','Paste','PasteText','PasteFromWord','SelectAll','Find','Replace'),
array('HorizontalRule','Smiley','SpecialChar','PageBreak','TextColor','BGColor'),
array('Link','Unlink','Anchor','Image','Table'),
array('Format','Font','FontSize'),
array('JustifyLeft','JustifyCenter','JustifyRight','JustifyBlock','NumberedList','BulletedList','Outdent','Indent'),
array('Bold','Italic','Underline','Strike','Blockquote','Subscript','Superscript','About')
),
);
if( $config['langeditor'] == 'inherit' ){
$defaults['language'] = $config['language'];
}else{
$defaults['language'] = $config['langeditor'];
}
// 3) $options
$options += $defaults;
// 2) Addon settings
$options = \gp\tool\Plugins::Filter('CKEditorConfig',array($options)); // $options['config_key'] = 'config_value';
$plugins = \gp\tool\Plugins::Filter('CKEditorPlugins',array($plugins)); // $plugins['plugin_name'] = 'path_to_plugin_folder';
// 1) User
$admin_config = self::CKAdminConfig();
foreach($admin_config['plugins'] as $plugin => $plugin_info){
$plugins[$plugin] = \gp\tool::GetDir('/data/_ckeditor/'.$plugin.'/');
}
// extra plugins
$extra_plugins = array_keys($plugins);
if( array_key_exists('extraPlugins',$options) ){
$extra_plugins = array_merge( $extra_plugins, explode(',',$options['extraPlugins']) );
}
$options = $admin_config['custom_config'] + $options;
$options['extraPlugins'] = implode(',',$extra_plugins);
//browser paths
if( $options['browser'] ){
$options['filebrowserBrowseUrl'] = \gp\tool::GetUrl('Admin/Browser').'?type=all';
$options['filebrowserImageBrowseUrl'] = \gp\tool::GetUrl('Admin/Browser').'?dir=%2Fimage';
unset($options['browser']);
}
switch( $config_name ){
case 'array':
return $options;
case 'json':
return json_encode($options);
}
return '$.extend('.$config_name.', '.json_encode($options).');';
}
/**
* Get the default content for the specified content type
* @static
* @since 3.6
*
*/
public static function DefaultContent($type='text', $heading = 'Lorem Ipsum' ){
global $langmessage;
$section = array();
$section['type'] = $type;
$section['content'] = '';
switch($type){
case 'include':
$section['gp_label'] = $langmessage['File Include'];
break;
case 'gallery':
$section['gp_label'] = $langmessage['Image Gallery'];
$section['content'] = '
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
'.$langmessage['Width'].' | '; echo ' | '.$langmessage['Height'].' | '; echo ' |
'.$langmessage['Left'].' | '; echo ' | '.$langmessage['Top'].' | '; echo ' |
⟲ |