<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>files/crontab</filename>
    </added>
    <added>
      <filename>files/dump/db/0-schema.sql</filename>
    </added>
    <added>
      <filename>files/dump/db/1-users.sql</filename>
    </added>
    <added>
      <filename>files/dump/db/2-licenses.sql</filename>
    </added>
    <added>
      <filename>files/dump/db/3-themes.sql</filename>
    </added>
    <added>
      <filename>files/dump/db/4-main-site.sql</filename>
    </added>
    <added>
      <filename>files/dump/db/5-template-site.sql</filename>
    </added>
    <added>
      <filename>files/dump/sites/www/admin.manage.page</filename>
    </added>
    <added>
      <filename>files/dump/sites/www/admin.superuser.page</filename>
    </added>
    <added>
      <filename>files/dump/sites/www/admin.users.page</filename>
    </added>
    <added>
      <filename>files/dump/sites/www/auth.login.page</filename>
    </added>
    <added>
      <filename>files/dump/sites/www/auth.newaccount.page</filename>
    </added>
    <added>
      <filename>files/dump/sites/www/auth.newaccount2.page</filename>
    </added>
    <added>
      <filename>files/dump/sites/www/auth.newaccount3.page</filename>
    </added>
    <added>
      <filename>files/dump/sites/www/how-to-edit-pages.page</filename>
    </added>
    <added>
      <filename>files/dump/sites/www/nav.side.page</filename>
    </added>
    <added>
      <filename>files/dump/sites/www/start.page</filename>
    </added>
    <added>
      <filename>files/dump/sites/www/system.list-all-pages.page</filename>
    </added>
    <added>
      <filename>files/dump/sites/www/system.page-tags.page</filename>
    </added>
    <added>
      <filename>files/dump/sites/www/system.recent-changes.page</filename>
    </added>
    <added>
      <filename>files/dump/sites/www/what-is-a-wiki.page</filename>
    </added>
    <added>
      <filename>php/class/Wikidot/Facade/Exception/NotImplemented.php</filename>
    </added>
    <added>
      <filename>php/class/Wikidot/Facade/Exception/WrongReturnValue.php</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -5,8 +5,9 @@ prepare_db:
 	bin/prepare_db.php | psql
 
 db:
-	bin/bootstrap_db.php files/singlewiki-dump.sql
+	bin/bootstrap_db.php files/dump/db/*.sql
 	bin/generate_om.php
+	bin/bootstrap_pages.php files/dump/sites/*
 
 config:
 	bin/configure.php</diff>
      <filename>Makefile</filename>
    </modified>
    <modified>
      <diff>@@ -32,4 +32,4 @@ require ('../php/setup.php');
 $host = GlobalProperties::$IP_HOST . ':' . GlobalProperties::$HTTP_PORT;
 $key = GlobalProperties::$SECRET_MANAGE_SUPERADMIN;
 
-echo &quot;http://$host/admin:superadmin/key/$key\n&quot;;
+echo &quot;http://$host/admin:superuser/key/$key\n&quot;;</diff>
      <filename>bin/finish_url.php</filename>
    </modified>
    <modified>
      <diff>@@ -46,6 +46,12 @@ abstract class Wikidot_Facade_Base {
 	
 	/**
 	 * 
+	 * @var bool
+	 */
+	protected $clear_parent_page = false;
+	
+	/**
+	 * 
 	 * @var string
 	 */
 	protected $title = null;
@@ -63,13 +69,37 @@ abstract class Wikidot_Facade_Base {
 	protected $tags = null;
 	
 	/**
+	 * 
+	 * @var array
+	 */
+	protected $config = array();
+	
+	/**
+	 * 
+	 * @var array
+	 */
+	protected $config_keys = array('expose_file_path');
+	
+	/**
 	 * construct Facade object
 	 * 
 	 * @param $performer DB_OzoneUser
+	 * @param $app string application
+	 * @param $config array configuration array, keys: expose_file_path: false by default
 	 */
-	public function __construct($performer = null, $app = null) {
+	public function __construct($performer = null, $app = null, $config = null) {
 		$this-&gt;performer = $performer;
 		$this-&gt;app = $app;
+		
+		if (is_array($config)) {
+			foreach ($this-&gt;config_keys as $key) {
+				if (isset($config[$key])) {
+					$this-&gt;config[$key] = $config[$key];
+				} else {
+					$this-&gt;config[$key] = null;
+				}
+			}
+		}
 	}
 	
 	/**
@@ -110,13 +140,13 @@ abstract class Wikidot_Facade_Base {
 					$this-&gt;parent_page = $value;
 					break;
 				case &quot;title&quot;:
-					$this-&gt;title = $this-&gt;_parseString($value, &quot;title&quot;);
+					$this-&gt;title = $this-&gt;_parseString($value, &quot;title&quot;, 128);
 					break;
 				case &quot;source&quot;:
-					$this-&gt;source = $this-&gt;_parseString($value, &quot;source&quot;);
+					$this-&gt;source = $this-&gt;_parseString($value, &quot;source&quot;, 200000);
 					break;
 				case &quot;tags&quot;:
-					$this-&gt;tags = $this-&gt;_parseTags($value);
+					$this-&gt;tags = $this-&gt;_parseTags($value, 64, 500);
 					break;
 				default:
 					throw new Wikidot_Facade_Exception_WrongArguments(&quot;Invalid argument array key: $key&quot;);
@@ -137,6 +167,10 @@ abstract class Wikidot_Facade_Base {
 			$this-&gt;parent_page = $this-&gt;_parsePage($this-&gt;site, $this-&gt;parent_page);
 		}
 		
+		if ($this-&gt;parent_page === &quot;&quot;) { // empty string is passed as the parent_page
+			$this-&gt;clear_parent_page = true;
+		}
+		
 		foreach ($requiredArgs as $key) {
 			if (! $this-&gt;$key) {
 				throw new Wikidot_Facade_Exception_WrongArguments(&quot;Required argument array key not passed: $key&quot;);
@@ -178,24 +212,44 @@ abstract class Wikidot_Facade_Base {
 		throw new Wikidot_Facade_Exception_WrongReturnValue(&quot;Invalid type of returned value&quot;);
 	}
 	
-	protected function _parseString($value, $key = &quot;&quot;) {
+	protected function _parseString($value, $key = &quot;&quot;, $max_length = null, $trim = true) {
+		if (is_numeric($value)) {
+			$value = &quot;$value&quot;;
+		}
 		if (is_string($value)) {
+			
+			if ($trim) {
+				$value= trim($value);
+			}
+			
+			if ($max_length &amp;&amp; strlen8($value) &gt; $max_length) {
+				throw new Wikidot_Facade_Exception_WrongArguments(&quot;Argument $key is too long (&gt; $max_length)&quot;);
+			} 
+			
 			return $value;
 		}
-		if (is_numeric($value)) {
-			return &quot;$value&quot;;
-		}
 		throw new Wikidot_Facade_Exception_WrongArguments(&quot;Argument $key must be a string&quot;);
 	}
 	
-	protected function _parseTags($tags) {
+	protected function _parseTags($tags, $max_tag_length = null, $max_total_length = null) {
 		if (is_string($tags)) {
 			$tags = preg_split(&quot;/[ ,]+/&quot;, trim($tags));
 		}
-		if (is_array($tags)) {
-			return $tags;
+		if (! is_array($tags)) {
+			throw new Wikidot_Facade_Exception_WrongArguments(&quot;Invalid tags argument (it must be array or string)&quot;);
+		}
+		$tags = array_unique($tags);
+		$total_length = -1;
+		$tags_new = array();
+		foreach ($tag as $tags) {
+			$tag = $this-&gt;_parseString($tag, &quot;tag&quot;, $max_tag_length);
+			$total_length += strlen8($tag) + 1;
+			$tags_new[] = strtolower($tag);
 		}
-		throw new Wikidot_Facade_Exception_WrongArguments(&quot;Invalid tags argument (it must be array or string)&quot;);
+		if ($total_length &gt; $max_total_length) {
+			throw new Wikidot_Facade_Exception_WrongArguments(&quot;Tags are too long (&gt; $max_total_length)&quot;);
+		}
+		return $tags_new;
 	}
 	
 	protected function _parseUser($user) {
@@ -203,6 +257,13 @@ abstract class Wikidot_Facade_Base {
 			$user = DB_OzoneUserPeer::instance()-&gt;selectByPrimaryKey($user);
 		}
 		
+		if (is_string($user)) {
+			$c = new Criteria();
+			$unix_name = WDStringUtils::toUnixName($user);
+			$c-&gt;add('unix_name', $unix_name);
+			$user = DB_OzoneUserPeer::instance()-&gt;selectOne($c);
+		}
+		
 		if ($user instanceof DB_OzoneUser) {
 			return $user;
 		}
@@ -386,15 +447,18 @@ abstract class Wikidot_Facade_Base {
 	 * @return array
 	 */
 	protected function _reprFile($file) {
-		return array(
+		$r = array(
 			&quot;url&quot; =&gt; $file-&gt;getFileURI(),
 			&quot;name&quot; =&gt; $file-&gt;getFilename(),
-			&quot;path&quot; =&gt; $file-&gt;getFilePath(),
 			&quot;mime&quot; =&gt; $file-&gt;getMimetype(),
 			&quot;description&quot; =&gt; $file-&gt;getDescription(),
 			&quot;comment&quot; =&gt; $file-&gt;getComment(),
 			&quot;date_added&quot; =&gt; $this-&gt;_reprDate($file-&gt;getDateAdded()),
 			&quot;size&quot; =&gt; $file-&gt;getSize(),
 		);
+		if ($this-&gt;config['expose_file_path']) {
+			$r['path'] = $file-&gt;getFilePath();
+		}
+		return $r;
 	}
 }</diff>
      <filename>php/class/Wikidot/Facade/Base.php</filename>
    </modified>
    <modified>
      <diff>@@ -39,72 +39,257 @@ class Wikidot_Facade_Page extends Wikidot_Facade_Base {
 		$files = DB_FilePeer::instance()-&gt;select($c);
 		
 		return $this-&gt;repr($files);
-	}
+    }
+
+    private function _getOrCreateCategory($site, $categoryName) {
+        $category = DB_CategoryPeer::instance()-&gt;selectByName($categoryName, $site-&gt;getSiteId(), false);
+        if ($category == null){
+            // create the category - just clone the default category!!!
+            $category = DB_CategoryPeer::instance()-&gt;selectByName(&quot;_default&quot;, $site-&gt;getSiteId(), false);
+            $category-&gt;setCategoryId(null);
+            $category-&gt;setNew(true);
+            $category-&gt;setName($categoryName);
+            // fill with some important things - we assume the _default category exists!!! IT REALLY SHOULD!!!
+            $category-&gt;setPerPageDiscussion(null); //default value
+            // set default permissions theme and license
+            $category-&gt;setPermissionsDefault(true);
+            $category-&gt;setThemeDefault(true);
+            $category-&gt;setLicenseDefault(true);
+            $category-&gt;setNavDefault(true);
+            $category-&gt;save();
+        }
+        return $category;
+     }
 	
 	public function save($args) {
 		
+		$db = Database::connection();
+		$db-&gt;begin();
+		
+		// simple argument checking
 		if (! isset($args['page'])) {
 			throw new Wikidot_Facade_Exception_WrongArguments(&quot;Page argument must be passed&quot;);
 		}
 		
-		$arg_page = $args['page'];
+		$pm = new WDPermissionManager();
+		$now = new ODate();
 		
+		// page (existant or not) name
+		$arg_page = WDStringUtils::toUnixName($args['page']);
+		
+		// parse the rest (beside page name)
 		unset($args['page']);
 		$this-&gt;parseArgs($args, array(&quot;performer&quot;, &quot;site&quot;));
 		
 		try {
 			
+			// parse page name to figure out if it points to an existant page
 			$page = $this-&gt;_parsePage($this-&gt;site, $arg_page);
 			
-			// page exists
 			$new = false;
+
+			// check permissions to edit the page
+			$pm-&gt;hasPagePermission('edit', $this-&gt;performer, $page-&gt;getCategory(), $page);
+
+		} catch (Wikidot_Facade_Exception_WrongArguments $e) {
+            if ($this-&gt;source === null) {
+                $this-&gt;source = &quot;&quot;;
+            }
+            if ($this-&gt;title === null) {
+                $this-&gt;title = $arg_page;
+            }
+            $new = true;
+
+            $category_name = preg_replace('/^([^:]*):.*$/', '\1', $arg_page);
+            if ($category_name == $arg_page) {
+                $category_name = '_default';
+            }
+            $category = $this-&gt;_getOrCreateCategory($this-&gt;site, $category_name);
+
+            $page = new DB_Page();
+            $page-&gt;setSiteId($this-&gt;site-&gt;getSiteId());
+            $page-&gt;setCategoryId($category-&gt;getCategoryId());
+            $page-&gt;setUnixName($arg_page);
+            $page-&gt;setDateCreated(new ODate());
+            $page-&gt;setOwnerUserId($this-&gt;performer-&gt;getUserId());
+            $page-&gt;save();
+
+            $compiled = new DB_PageCompiled();
+            $compiled-&gt;setPageId($page-&gt;getPageId());
+            $compiled-&gt;save();
+		}
+		
+    	// get current revision and metadata
+        if (! $new) {
+    		$cur_rev = $page-&gt;getCurrentRevision();
+	        $cur_meta = $cur_rev-&gt;getMetadata();
+        }
 			
-			// check permision to edit here
+		// construct new metadata
+        if ($new) {
+            $new_meta = new DB_PageMetadata();
+            $new_meta-&gt;setUnixName($arg_page);
+            $new_meta-&gt;setOwnerUserId($this-&gt;performer-&gt;getUserId());
+        } else {
+    		$new_meta = clone $cur_meta;
+	    	$new_meta-&gt;setNew(true);
+    		$new_meta-&gt;setMetadataId(null);
+        }
+		
+        // construct new revision
+		$new_rev = new DB_PageRevision();
+		$new_rev-&gt;setSiteId($this-&gt;site-&gt;getSiteId());
+		$new_rev-&gt;setPageId($page-&gt;getPageId());
+		$new_rev-&gt;setDateLastEdited($now);
+        if ($new) {
+            $new_rev-&gt;setRevisionNumber(0);
+        } else {
+    		$new_rev-&gt;setRevisionNumber($cur_rev-&gt;getRevisionNumber() + 1);
+        }
+		
+		$src_changed = false;
+		$title_changed = false;
+		$parent_changed = false;
+		$tags_changed = false;
+		
+		// handle source change
+		if ($new || ($this-&gt;source !== null &amp;&amp; $page-&gt;getSource() != $this-&gt;source)) {
 			
-		} catch (Wikidot_Facade_Exception_WrongArguments $e) {
+			$new_src = new DB_PageSource();
+			$new_src-&gt;setText($this-&gt;source);
+			$new_src-&gt;save();
 			
-			// page does not exist
-			$new = true;
+			$new_rev-&gt;setSourceId($new_src-&gt;getSourceId());
+			$new_rev-&gt;setFlagText(true);
 			
-			// check permission to create here
+			$src_changed = true;
+		
+		} else {
+			
+			$new_rev-&gt;setSourceId($cur_rev-&gt;getSourceId());
+			$new_rev-&gt;setSinceFullSource($cur_rev-&gt;getSinceFullSource());
+			$new_rev-&gt;setDiffSource($cur_rev-&gt;getDiffSource());
 			
-			// create
-			$page = new DB_Page();
-			// ...
 		}
 		
-		if ($this-&gt;title === null &amp;&amp; ! $this-&gt;tags &amp;&amp; ! $this-&gt;parent_page &amp;&amp; $this-&gt;source === null) {
+		// handle tags change
+		if ($this-&gt;tags) {
+			
+			$new_tags = $this-&gt;tags;
+			$cur_tags = $page-&gt;getTagsAsArray();
 			
-			if ($new) {
-				// save a new page, but only page_name is passed
+			sort($cur_tags);
+			sort($new_tags);
+			
+			if ($cur_tags != $new_tags) {
+				$tags_changed = true;
+				$tags_deleted = array();
+				$tags_added = array();
 				
-			} else {
-				// no changes
-				// maybe recompile?
-				//
-				// or just throw Wikidot_Facade_Exception_WrongArgument
+				foreach ($cur_tags as $tag) {
+					if (! in_array($tag, $new_tags)) {
+						
+						$c = new Criteria();
+						$c-&gt;add('page_id', $page-&gt;getPageId());
+						$c-&gt;add('tag', $tag);
+						
+						if ($t = DB_PageTagPeer::instance()-&gt;selectOne($c)) {
+							$t-&gt;delete();
+							$tags_deleted[] = $tag;
+						}
+					}
+				}
 				
+				foreach ($new_tags as $tag) {
+					if (! in_array($tag, $cur_tags)) {
+						$t = new DB_PageTag();
+						$t-&gt;getPageId($page-&gt;getPageId());
+						$t-&gt;setSiteId($this-&gt;site-&gt;getSiteId());
+						$t-&gt;setTag($tag);
+						$t-&gt;save();
+						
+						$tags_added[] = $tag;
+					}
+				}
 			}
-			
-			return;
 		}
 		
-		if ($this-&gt;source !== null) {
-			// set source
+		// handle metadata: title change
+		if ($new || ($this-&gt;title !== null &amp;&amp; $cur_meta-&gt;getTitle() != $this-&gt;title)) {
+			
+			$new_meta-&gt;setTitle($this-&gt;title);
+			$page-&gt;setTitle($this-&gt;title);
+			$title_changed = true;
 		}
 		
-		if ($this-&gt;title !== null) {
-			// set title
+		// handle metadata: parent page change
+		if ($this-&gt;parent_page) {
+			if (! $cur_meta-&gt;getParentPageId() || 
+			    $cur_meta-&gt;getParentPageId() != $this-&gt;parent_page-&gt;getPageId()
+			) {
+				$new_meta-&gt;setParentPageId($this-&gt;parent_page-&gt;getPageId());
+				$parent_changed = true;
+			}
 		}
-		
-		if ($this-&gt;tags) {
-			// set tags
+		if ($this-&gt;clear_parent_page &amp;&amp; $page-&gt;getParentPageId()) {
+			$new_meta-&gt;setParentPageId(null);
+			$parent_changed = true;
 		}
 		
-		if ($this-&gt;parent_page) {
-			// set parent_page
+		$meta_changed = $title_changed || $parent_changed;
+		
+		// decide whether to use previous metadata or create a new object
+		if ($meta_changed) {
+			
+			$new_meta-&gt;save();
+			$new_rev-&gt;setMetadataId($new_meta-&gt;getMetadataId());
+			
+		} else {
+			$new_rev-&gt;setMetadataId($cur_meta-&gt;getMetadataId());
 		}
 		
-		// save new revision and stuff
+		if ($src_changed || $meta_changed || $tags_changed) {
+			
+			$new_rev-&gt;save();
+			
+			$page-&gt;setSourceId($new_rev-&gt;getSourceId());
+			$page-&gt;setDateLastEdited($now);
+			$page-&gt;setMetadataId($new_rev-&gt;getMetadataId());	
+			$page-&gt;setRevisionNumber($new_rev-&gt;getRevisionNumber());
+			$page-&gt;setRevisionId($new_rev-&gt;getRevisionId());
+			$page-&gt;save();
+			
+			$db-&gt;commit();
+		
+            $GLOBALS['site'] = $this-&gt;site;
+			$outdater = new Outdater();
+			if ($src_changed) {
+				$outdater-&gt;pageEvent(&quot;source_changed&quot;, $page);
+			}
+			if ($title_changed) {
+				$outdater-&gt;pageEvent(&quot;title_changed&quot;, $page);
+			}
+			if ($parent_changed) {
+				$outdater-&gt;pageEvent(&quot;parent_changed&quot;, $page);
+			}
+			if ($tags_changed) {
+				$outdater-&gt;pageEvent(&quot;tag_changed&quot;, $page);
+			}
+			
+		} else {
+			
+			/* This place is reached when API client tries to set source or
+			 * title or parent page or tags that are already set (in the DB)
+			 * to the same value.
+			 * 
+			 * Let's suppose doing nothing is the desired behavior in this case
+			 * 
+			 * Other possible way to react can be raising an exception.
+			 * But it should be different from Wikidot_Facade_Exception_WrongArguments
+			 * because this one implies client error (and client does not need
+			 * to know the exact database state).
+			 */
+			
+		}
 	}
 }</diff>
      <filename>php/class/Wikidot/Facade/Page.php</filename>
    </modified>
  </modified>
  <removed type="array">
    <removed>
      <filename>files/db_dump/0-schema.sql</filename>
    </removed>
    <removed>
      <filename>files/db_dump/1-users.sql</filename>
    </removed>
    <removed>
      <filename>files/db_dump/2-licenses.sql</filename>
    </removed>
    <removed>
      <filename>files/db_dump/3-themes.sql</filename>
    </removed>
    <removed>
      <filename>files/db_dump/4-main-site.sql</filename>
    </removed>
    <removed>
      <filename>files/db_dump/5-template-site.sql</filename>
    </removed>
    <removed>
      <filename>files/farm-dump.sql</filename>
    </removed>
    <removed>
      <filename>files/singlewiki-dump.sql</filename>
    </removed>
    <removed>
      <filename>php/class/Wikidot/Facade/Exception/ReturnValueException.php</filename>
    </removed>
  </removed>
  <parents type="array">
    <parent>
      <id>8be37649fec381a26370cbd16f6a73f69348e6f8</id>
    </parent>
  </parents>
  <author>
    <name>Piotr Gabryjeluk</name>
    <email>piotr@wikidot.com</email>
  </author>
  <url>http://github.com/gabrys/wikidot/commit/4c4cdb34e9deb21e8c04ebbaef9fd1f2c110af5b</url>
  <id>4c4cdb34e9deb21e8c04ebbaef9fd1f2c110af5b</id>
  <committed-date>2009-06-19T17:02:38-07:00</committed-date>
  <authored-date>2009-06-19T17:02:38-07:00</authored-date>
  <message>introduced new dump load system</message>
  <tree>3527fa37ac6666120b4c257464ae8f489501e822</tree>
  <committer>
    <name>Piotr Gabryjeluk</name>
    <email>piotr@wikidot.com</email>
  </committer>
</commit>
