<?php if (!defined('PmWiki')) exit();

/**********************************************************************
version 1.6 2017-12-10
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	 Copyright 2004 Larry Baltz <larry+pmwiki@baltz.org>
	
	 This program is free software; you can redistribute it and/or modify
	 it under the terms of the GNU General Public License as published by
	 the Free Software Foundation; either version 2 of the License, or
	 (at your option) any later version.
	
	 This program is distributed in the hope that it will be useful,
	 but WITHOUT ANY WARRANTY; without even the implied warranty of
	 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the
	 GNU General Public License for more details.
	
	 You should have received a copy of the GNU General Public License
	 along with this program; if not, write to the Free Software
	 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA	02111-1307	USA
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	
 Description:
	 This file is intended to be used as a "cookbook" extension to 
	 pmwiki 2, and is used to provided upload and display of CSV 
	 table data directly on a PmWiki page.
	
 Usage:
	 Put the following line in your local/config.php file
	 include_once("cookbook/CSVInclude.php");

	 Inline Wiki mark-up can be used in the table too.	
	 Block mark-up like bullets, indentation and heders will not work.
	 The limitations are due to rendering the table using the 
	 simple table format.
	
	 There is some special mark-up you can include in your source CSV 
	 table:
		 * use <<del>> to cause the cell to join with previous cell.
			 This stands for del(ete)
		 * use \\ in the text to denote a line break

*/

$RecipeInfo['CSVInclude']['Version'] = '2020-06-24';

## This file is dependent on some variables and fuctions in the upload script.
include_once("$FarmD/scripts/upload.php");

SDV($CSVIncludeLocalCachingEnabled, true);
SDV($CSVIncludeLocalCacheFolder, 'uploads/csvincludecache/');
SDV($CSVIncludeDefaultCacheExpiration, 300);
SDV($CSVIncludeFollowRedirects, true);

# If the user has edit permissions to the current page, and has triggered cache 
# invalidation via the csv_include_invalidate_caches GET parameter, delete all cached 
# files associated with attachcsv directives on the current page.
if ($_GET['csvinclude_clear_cache'] == 1) {
	$page = RetrieveAuthPage($pagename, 'read', false, READPAGE_CURRENT);
	if ($page['=auth']['edit']) {
		global $CSVIncludeLocalCacheFolder;
		preg_match_all("/\\[:attachcsv\\s+([^']+?)('[^']*')?(\\s+?expire=\\d+)?:\\]/", $page['text'], $matches);
		foreach ($matches[1] as $upname) {
			if (preg_match("/^https?:/",$upname)) {
				$url_hash = md5($upname);
				$filepath = $CSVIncludeLocalCacheFolder.$url_hash.'.csv';
				@unlink($filepath);
			}
		}
	}
	Redirect($pagename);
}				

## [:attachcsv:]
Markup('attachcsv','>if',"/\\[:(attachcsv\\s+.+?):\\]/", "AttachCSVDirectiveParse");
function AttachCSVDirectiveParse($m) {
	global $pagename;
	return PRR().AttachCSV($pagename,$m[1]);
}

/*
Function: AttachCSV
 
Description:
	takes a specification for a CSV file from the wiki mark-up and 
	generates wiki mark-up from the file and includes it into the 
	wiki mark-up steam.	This allows the inclusion of wiki mark
	up in the CSV file itself.

	CSV files are drawn from the upload directery consitant with 
	the group the file is referenced from.	If a file is specifed
	that does not exist, an "[[Attach:]] is generated for the file
	to allow the upload of the file.	Once the file exists, the
	CSV content is converted and included in the body of the 
	wiki page.

	Mark-up syntax:
		[:attachcsv<white space><filename>('<table attributes>'):]
 
	Mark-up examples:
		[:attachcsv bob.csv:]
		[:attachcsv boby sue.csv:]
		[:attachcsv billy bob.csv'border=1 width=100%':]

*/
function AttachCSV($pagename, $csvspec) {
	global $AttachCSVBadAnchorFmt, $Newline, $UploadFileFmt, $FmtV;
	SDV($AttachCSVBadAnchorFmt, "<a class='createlinktext' href='\$LinkUrl'>\$LinkText</a><a class='createlink' href='\$LinkUrl'>&nbsp;&#8657;</a>");

	# Parse the the mark-up for file name and attributes
	if(preg_match("/^attachcsv\\s+([^']+)('([^']*)')?(\\s+?expire=(\\d+))?$/", $csvspec, $match)) {
		$upname = preg_replace("/&amp;/", "&", rtrim($match[1]));

		# deal with empty attribute list
		$tableAttributes = isset($match[3]) ? $match[3] : "";

		# Check if it's a URL.
		if (preg_match("/^https?:/",$upname)) {
			global $CSVIncludeLocalCachingEnabled;
			
			# If local caching is enabled, then we check to see if we have the remote CSV
			# cached locally; if not, or if the locally cached version exists but needs 
			# to be invalidated, then we retrieve the remote version and cache it.
			if ($CSVIncludeLocalCachingEnabled) {				
				global $CSVIncludeLocalCacheFolder, $CSVIncludeDefaultCacheExpiration, $CSVIncludeFollowRedirects;

				# Create the cache directory, if it doesn't exist.
				if (!file_exists($CSVIncludeLocalCacheFolder))
					mkdir($CSVIncludeLocalCacheFolder);
				
				# Hash the URL (we use it as the filename for the locally cached version).
				$url_hash = md5($upname);
				$filepath = $CSVIncludeLocalCacheFolder.$url_hash.'.csv';
				
				# We check whether we need to retrieve the remote file (i.e., if we 
				# don't already have it, or if we do have it but we need to invalidate
				# the cached version).
				$retrieve_remote_file = true;
				if (file_exists($filepath)) {
					# Get cached file's modification date and compare age to specified 
					# cache expiration time.
					# If the expiration time is set to a non-positive value, that means
					# "always invalidate" (a.k.a. "don't cache"). 
					# (Isn't this equivalent to setting $CSVIncludeLocalCachingEnabled to
					#  false? No, because this way, a user can disable caching for a
					#  *particular* [:attachcsv:] call.)
					global $Now;
					$max_age = isset($match[5]) ? $match[5] : $CSVIncludeDefaultCacheExpiration;
					$retrieve_remote_file = ($max_age <= 0 || ($Now - filemtime($filepath)) > $max_age);
				}
				
				# If the given URL's content isn't cached, or the locally cached version 
				# needs to be invalidated, retrieve it.
				if ($retrieve_remote_file == true) {
					if ($CSVIncludeFollowRedirects)
						`curl -L -o $filepath $upname`;
					else
						`curl -o $filepath $upname`;
				}
									
			} else {
				# If local caching is not enabled, we simply retrieve the CSV from the 
				# remote URL with every page load.
				$filepath = $upname;
			}
		} else {
			# create file reference consistent with upload feature
			$filepath = FmtPageName("$UploadFileFmt/$upname", $pagename);
		
			# if the CSV file does not exist, generate an Attach directive to all
			# the upload of the file
			if(!file_exists($filepath)) {
				return("[[Attach:".$upname."]]");
			}
		}

		# Open the CSV file and prep for parsing each line
		$csvFile = fopen($filepath, "r");
		$rowNum = 0;
		$text = "||".$tableAttributes."\n";

		# loop through each row and column and generate wiki simple table mark-up
		while($data = fgetcsv($csvFile,10000)) {
			$cellNum=0;
			for($cellNum = 0; $cellNum < count($data); $cellNum++) {
				$cellContents = $data[$cellNum];
				if($cellContents == "") {
					$cellContents = " ";
				}
				else if($cellContents == "<<del>>") {
					$cellContents = "";
				} 
				else {
					 ## turn "\\" into line break and 
					 ## escape sequences of "||..."
					 $cellContents = preg_replace(array("/\\\\\\\\/","/(\\|\\|+)/"),
																				array("\\\\\\\\\n","[=\\1=]"),
																				$cellContents); 
				}
				$text .= "||".$cellContents;
			}
			$text .= "||\n";
		}

		$text .= "\n";
		
		return $text;
	}
	
	# just return the raw mark-up if it can't be parsed
	return("[:".$csvspec.":]");
}

## Make sure the CSV mime type is set
if(!isset($UploadExts['csv'])) {
	$UploadExts['csv'] = 'text/plain';
}
if(!isset($UploadExtSize['csv'])) {
	$UploadExtSize['csv'] = isset($UploadMaxSize) ? $UploadMaxSize : 50000;
}

?>