Extension:UploadCSV/UploadCSV.php

From MediaWiki.org
Jump to navigation Jump to search
<?php
# Extension:UploadCSV{{php}}{{Category:Extensions|UploadCSV}}
# - See http://www.mediawiki.org/wiki/Extension:UploadCSV for installation and usage details
# - Author: Gunter Schmidt (http://www.mediawiki.org/wiki/User:GunterS)

if ( !defined( 'MEDIAWIKI' ) ) {
?>
<p>This is an MediaWiki-extension. To enable it, put </p>
<pre>require_once("$IP/extensions/UploadCSV/UploadCSV.php");</pre>
<p>near the bottom of your LocalSettings.php</p>
<?php
	exit(1);
}

$Ext_Version = '0.2.3'; // Mar. 27, 2007
$wgExtensionFunctions[] = "wfUploadCSVStart";
$wgExtensionCredits['specialpage'][] = array(
	'name' => 'Special:UploadCSV',
	'description' => 'Mass creation of articles from a csv-file.',
	'url' => 'http://www.mediawiki.org/wiki/Extension:UploadCSV',
	'author' => 'Gunter Schmidt',
	'version' => $Ext_Version
);

/* ToDo
- smart edit of existing articles, only replace template
- multiple templates on a page (maybe template given on the dataline, will need a template key)
- translation
- documentation
*/

require_once "$IP/includes/SpecialPage.php";
# Internationalisation file
require_once( dirname( __FILE__ ) . '/UploadCSV.i18n.php' );

function wfUploadCSVStart() {
	global $wgMessageCache, $ExtensionTitles;

	// Load messages, so we have a title in Special:SpecialPages
	UploadCSVPage::loadTitles();

	SpecialPage::addPage(new UploadCSVPage());
}

class UploadCSVPage extends SpecialPage {
		public $Specialpage_Name='UploadCSV', $MsgPrefix; //keep this within the class or it will mess with other extensions!
		public $curRow;
		
	function UploadCSVPage() {
		SpecialPage::SpecialPage($this->Specialpage_Name);
				$this->MsgPrefix = strtolower($this->Specialpage_Name) . '_';
	}

	function execute( $par ) {
		global $wgRequest, $wgOut, $wgUser, $csvInfo, $wgUploadDirectory;
		$MsgPrefix = $this->MsgPrefix;

		//Debug info
		$hidden = "text"; // hidden or text

		self::loadMessages();
		$this->setHeaders();

		#$wgOut->setPagetitle("Gunters Extension"); // only necessary if you do want a different title than in the Special:Specialpages list
		$wgOut->addHTML( wfMsg( $MsgPrefix . 'intro' ) );
				
		// Set form variables
		$template = $wgRequest->getText('wpTemplate');
		if (empty($template)) $template = "SAP-Jobstep"; #else $template = "ok";

		# Put all templates into a select list
		$templates = '';
		$db =& wfGetDB(DB_SLAVE);
		$pagetable = $db->tableName('page');
		$ns = NS_TEMPLATE;
		$result = $db->query("SELECT page_id FROM $pagetable WHERE page_namespace=$ns ORDER BY page_title");
		while ($row = mysql_fetch_array($result)) {
			$t = Title::newFromID($row[0])->getPrefixedText();
			$selected = $t == $template ? ' selected' : '';
			$templates .= "<option value='$t'$selected>$t</option>\n";
			}

		$UploadFile = $wgRequest->getText('wpUploadFile');

		$curInfo = $wgRequest->getText('wpcurInfo');

		$curAction = $wgRequest->getText('wpcurAction');
		if (empty($curAction)) $curAction = 'start';
		$this->curRow = $wgRequest->getText('wpcurRow');
		if (empty($this->curRow)) $this->curRow = 1;
		$endRow = $wgRequest->getText('wpendRow');
		if (empty($endRow) || !is_numeric($endRow)) $endRow = 0;

		# Allow separator character to be specified in form and allow escaping in content, eg \;
		$separator = $wgRequest->getText('wpSeparator');
		if (empty($separator)) $separator = ';';
		$esc = $separator == '|' ? '\\' : '';
		$sepPattern = "/(?<!\\\\)$esc$separator/";

		# If file has only just been uploaded, move it to a permenant location
		# - the files are currently not deleted
		if ($curAction != 'start') $curUploadFile = $wgRequest->getText('wpcurUploadFile');
                elseif ($tmp = $wgRequest->getFileTempname('wpUploadFile')) {
                        if (is_uploaded_file($tmp)) {
                            $curUploadFile = "$wgUploadDirectory/".basename($tmp);
                            if (move_uploaded_file($tmp,$curUploadFile) === false) $errormsg = 'Failed to move tmp file!';
                            }
                        else $errormsg = 'Invalid upload file!';
                        }
                else $curUploadFile = '';
		$UploadFile = $curUploadFile;

		// build form
		$wgOut->addHTML("<form name=\"userForm\" action=\"$this->action\" method=\"post\" enctype=\"multipart/form-data\"><br />" .
			"<table style=\"text-align: left;\" border=\"0\" cellpadding=\"2\" cellspacing=\"0\">
			<tbody>

			<tr>  
				<td align='right'><b>" . wfMsg( 'nstab-template' ) . ":</b></td>
				<td><select name='wpTemplate'>$templates</select>
				<b>" . wfMsg( $MsgPrefix . 'separator' ) . ":</b> " .
				wfElement( 'input', array(
				'type'  => 'text',
				'name'  => 'wpSeparator',
							'size'  => '1',
				'value' => $separator
				)) .
				"</td>
			</tr>

			<tr>  
				<td align='right'><b>" . wfMsg( 'sourcefilename' ) . ":</b></td>  
				<td>" .
				wfElement( 'input', array(
				'type'  => 'file',
				'name'  => 'wpUploadFile',
							'size'  => '70',
							'maxlength' => '150',
				'value' => $UploadFile,
				'onchange' => "this.form.wpcurUploadFile.value = this.form.wpUploadFile.value"
				)) .
				"</td> 
			</tr>

			<tr>
				<td align='right'><b>" . wfMsg( $MsgPrefix . 'startline' ) . ":</b></td>
				<td>" .
				wfElement( 'input', array(
				'type'  => 'text',
				'name'  => 'wpcurRow',
							'size'  => '5',
				'value' => $this->curRow
				)) .
				"</td>
			</tr>

			<tr>  
				<td align='right'><b>" . wfMsg( $MsgPrefix . 'endline' ) . ":</b></td>  
				<td>" .
				wfElement( 'input', array(
				'type'  => 'text',
				'name'  => 'wpendRow',
							'size'  => '5',
				'value' => $endRow
				)) .
				"</td>
			</tr>

			<tr>  
				<td align='right'><b>" . wfMsg( $MsgPrefix . 'status' ) . ":</b></td>  
				<td>" .
				wfElement( 'input', array(
				'type'  => 'text',
				'name'  => 'wpcurAction',
							'size'  => '20',
							'readonly' => 'readonly',
				'value' => $curAction
				)) .
				"</td> 
			</tr>
			
			<tr>
				<td/>
				<td>" .
				wfElement( 'input', array(
				'type'  => 'submit',
				'name'  => 'create',
				'value' => wfMsgHtml( 'uploadbtn' )
				)) .

				// hidden elements
				wfElement( 'input', array(
				'type'  => "$hidden",
				'name'  => 'wpcurUploadFile',
							'size'  => '20',
							'maxlength' => '150',
				'value' => $curUploadFile
						)) .

				wfElement( 'input', array(
				'type'  => "$hidden",
				'name'  => 'wpcurInfo',
							'size'  => '20',
				'value' => $curInfo
				)) .
				"</td>
			</tr>
			</tbody>
			</table>
			</form>\n"
			);

		// do the works
		// if ($this->curRow > 1) 
			// $wgOut->addHTML("<b>Finished uploading data for article no. " . ($this->curRow + 1) . ".</b>");
			// $wgOut->AddHTML ('<script type="text/javascript">
					// var jscount = document.userForm.wpcurRow.value;
					// document.write("<b>Uploading data for article no2. " +
					// jscount +
					// ".Ende</b>");
					// </script>');
		unset($errormsg);
		$editAllowed = $wgUser->isAllowed('edit');
		if ($editAllowed) {
			if ($handle = fopen($UploadFile, "r")) {

				// search for pagetitle
				$headerline = fgets($handle);
				$headers = preg_split($sepPattern,trim($headerline));
				unset ($colPageTitle);
				foreach ($headers as $key => $header) {
					$headers[$key] = trim($header);
					if ( stripos($header, "pagetitle") === 0 ) $colPageTitle = $key;
				}

				// Loop through the each line of the file if we have a pagetitle column
				if (isset($colPageTitle)) {
					$cRow = 0;
					$sRow = $this->curRow;
					if (!is_numeric($this->curRow)) $this->curRow = 0;
					while ($dataline = fgets($handle)) {
						$cRow += 1;

						// Process the line if it's not before the start-line
						if ($cRow >= $this->curRow) {
							$this->curRow = $cRow;
							$data = preg_split($sepPattern,trim($dataline));
							$curInfo += 1;
							if (is_array($data)) {
								$pageTitle = trim($data[$colPageTitle]);
								if (!empty($pageTitle)) {
									$ret = $this->MakeArticle($pageTitle, $template, $headers, $data);
									if ($ret !== true)
										$wgOut->AddWikiText("Error creating article: $ret");
								}
							}
						}

						// check, if we are through
						if (feof($handle) || ($this->curRow >= $endRow && $endRow > 0)) {
							$wgOut->addHTML('<script type="text/javascript">
								if (document.userForm.wpcurAction.value == "start") {
									document.userForm.wpcurInfo.value = 1; }
								else { document.userForm.wpcurInfo.value = ' . $curInfo . '}
								document.userForm.wpcurAction.value = "running";
								document.userForm.wpcurUploadFile.value = "' . addslashes($UploadFile) . '";
								document.userForm.wpcurRow.value = "' . ($this->curRow+1) . '";
								document.userForm.wpendRow.value = "' . (2*$this->curRow-$sRow+1) . '";
								</script>');
							unset($UploadFile);
							$bolFinished = true;
							$this->curRow = 0;
							break;
						}
					}
				}
				else $errormsg = "One column needs to be defined as \"pagetitle\".";
				fclose ($handle); 							
			}
			elseif ($UploadFile) $errormsg = "Couldn't open file.";
		}
		else $errormsg = "Not allowed to edit. Please log in first.";

		if (isset($errormsg)) $wgOut->AddWikiText("'''Error: $errormsg'''");

		// Output
		$output = "<br />" . wfMsg( $MsgPrefix . 'lastmessage' );
		#$wgOut->addHTML( $output );

		//automatically repeat function until upload is finished
		if (0 && $this->curRow > 1 && !empty($UploadFile)) {
			#echo "<br>c: $this->curRow";
			#sleep (2);
			$wgOut->addHTML('<script type="text/javascript">
				document.userForm.submit();
				</script>');
		} 
		elseif ($bolFinished)
			$txt = wfMsg( $MsgPrefix . 'finished' );
			$txt = ereg_replace('\$1', utf8_encode($curInfo), $txt);
			$wgOut->addHTML("<br /><b>$txt</b>");

	}
		
	function MakeArticle($pageTitle, $templateName, $tvars, $data, $summary='') {
		global $wgOut;
		// $pageTitle: Article name
		// $templateName: Full name of template, including Template:
		// $tvars: array of template variables
		// $tvarCol: array of column numbers corresponding to $tvars
		// $data: array of data
		// $summary: article change summary information
		$MsgPrefix = $this->MsgPrefix;

		$summary .= " (auto upload csv)";
		$summary = trim($summary);
		// get template title without "template:"
		if (preg_match("/:(.*)/", $templateName, $ret ) ) {
			$templateTitle = $ret[1];
			#print "<br>$templateTitle";
		}
		else $templateTitle = $templateName;

		//create content
		$content = '{{' . "$templateTitle\r\n";

		//nice output format
		$maxlen = 0;
		foreach ( $tvars as $tvar ) {
			if ( mb_strlen ($tvar) > $maxlen ) $maxlen = mb_strlen ($tvar);
		}
		//populate vars
		foreach ( $tvars as $key => $tvar ) {
			$space = str_repeat(" ", $maxlen - mb_strlen($tvar));
			$content .= "|$tvar$space = " . utf8_encode($data[$key]) . "\r\n";
		}
		$content .= '}}' . "\r\n";# . time() . "\r\n";
		#print "$content";

		//create article
		$titleNew = title::newFromText(htmlentities($pageTitle));
		$articleNew = new Article( $titleNew );
		if( $titleNew->getArticleID() == 0 ) {
			$mode = EDIT_NEW;
			$ctext = 'created';
		}
		else {
			$mode = EDIT_UPDATE;
			$ctext = 'changed';
			// smart edit, only replace template
			$t = explode('{{', $articleNew->getContent()); //all template starts
			echo "<br>";
			#print_r ($t); echo "<br>";
			if (count($t) > 1) {
				//find our template, for now expect only one
				$start = true;
				$tc = '';
				foreach ( $t AS $key => $x )
				{
					$xl = ltrim($x);
					if (stripos($xl, $templateTitle)=== 0 ) {
						// $foundkey = $key;
						// now we need the end, but there might be templates within the template or following templates
						$start = false;
						$te = explode('}}', $x);
						#print_r($te); echo "<br>";
						if ( count($te) == 2 ) { //no included templates, if there are, count = 1
							$tc .= substr($content,2) . $te[1];
						}
						else $tc .= implode('', $te);
						// foreach ( $te as $key2 => $y ) {

						// }
					}
					else $tc .= $x . '{{'; // rebuild content
				}
				$content = $tc;

				// $y = explode ( '}}' , $x , 2 ) ;
				// echo "<br>";
				// print_r ($y);
				// if ( count ( $y ) == 2 )
				// {
					// $z = $y[0] ;
					// $z = explode ( '|' , $z ) ;
					// $tn = array_shift ( $z ) ;
					// $t[$key] = '{{' . $x ;
				// }
				// else if ( $key != 0 ) $t[$key] = '{{' . $x ;
				// else $t[$key] = $x ;
			}
			else $content = implode('', $t) . $content; //no template found, so we add to the end

			#if (count($t) == 2) { //simple, only one template

			// $content_old = $articleNew->getContent();
			// $reg = '/({{' . $templateTitle . '.*?}})/';
			// $reg = '/.*?(' . $templateTitle . '.*?)B/';
			// $reg = '/Sofort([^}]*})/';
			// echo "<br>";
			// if (preg_match($reg, $content_old, $regs))
				// print_r ($regs);
			// else print "Fehler<br>$content_old<br>$reg";
		}

		// create/update article

		$createSuccess = $articleNew->doEdit( $content, $summary, $mode ); #| EDIT_FORCE_BOT 
		//sleep (2); // wait between  mass creation
		if ($createSuccess) {
			$cr = $this->curRow;
			$txt = 	wfMsg( $MsgPrefix . $ctext );
			$txt = ereg_replace('\$1', utf8_encode($cr), $txt); 
			$txt = ereg_replace('\$2', utf8_encode($pageTitle), $txt);
			$wgOut->addHTML("<p>$txt</p>");
			return true;
		}
	}
		

	function LoadTemplate($templateName='') {
		global $namespaceNames, $wgOut;

		//accept only templates, with or without "template:"
		// echo "<br>Temp: " . Namespace::getCanonicalName(NS_TEMPLATE) . ", " . NS_TEMPLATE ;
		// $tName = str_replace($namespaceNames[NS_TEMPLATE] . ':', '', $templateName);
		// $tName = $namespaceNames[NS_TEMPLATE] . ':' . $tName;
		// if ($tName <> $templateName) 
			// $wgOut->addHTML('<script type="text/javascript">
				// document.userForm.wpTemplate.value = "' . $tName . '";
				// </script>');
		#print "Template: $templateName";
		$title = title::newFromText($templateName);
		$pageID = $title->getArticleID();
		//Maybe the string template: was not given
		if ($pageID == 0) {
			$title = title::newFromText($templateName, NS_TEMPLATE);
			$pageID = $title->getArticleID();
		}
		if ($pageID) {
			#print "page: $pageID";
			$article = new Article( $title );
			$text = $article->getContent();
			$reg = '/{{{(.+?)}}}/';
			$varExist = preg_match_all($reg, $text, $tvars, PREG_PATTERN_ORDER);
			#print_r ($tvars);
			if ($varExist) {
				// Variables may be used multiple times in an template, we need to delete all double entries.
				$ret = array_unique($tvars[1]);
				return $ret; //return array of template vars; does not check on <nowiki>
			}
			else return "novars";
		}
		else return "notemplate";
	}

	function loadMessages() {
		self::loadMessagesS( 'messages' );
	}

	function loadTitles() {
		self::loadMessagesS( 'titles' );
	}
		
	function loadMessagesS( $MsgType )  {
		// using a function to save some memory instead of a global for the messages
		global $wgMessageCache;
		$Specialpage_Name='UploadCSV'; //needs to be defined here, because it is not always called in an object context
		$extensionName = strtolower($Specialpage_Name);

		//Need to change the function name accordingly
		$Messages = SpecialUploadCSV_Messages( $MsgType, $Specialpage_Name );

		if ( count ($Messages) == 0 ) return;
		foreach ( $Messages as $lang => $langMessages ) {
				foreach ( $langMessages as $key => $langMessage ) 
					$wgMessageCache->addMessage( $key, utf8_encode( $langMessage ), $lang);
		}
		return; 
	}
}