Example #1
0
		static string MakeTopicName(LocalTopicName name)
		{
			if (name.Version == null || name.Version.Length == 0)
				return name.Name;
			else
				return name.Name + "(" + name.Version + ")";
		}
Example #2
0
		/// <summary>
		/// Answer whether a topic exists and is writable
		/// </summary>
		/// <param name="topic">The topic (must directly be in this content base)</param>
		/// <returns>true if the topic exists AND is writable by the current user; else false</returns>
		public abstract bool IsExistingTopicWritable(LocalTopicName topic);
Example #3
0
		/// <summary>
		/// Write a new version of the topic (doesn't write a new version).  Generate all needed federation update changes via the supplied generator.
		/// </summary>
		/// <param name="topic">Topic to write</param>
		/// <param name="content">New content</param>
		/// <param name="sink">Object to recieve change info about the topic</param>
		override protected void WriteTopic(LocalTopicName topic, string content, FederationUpdateGenerator gen)
		{
			string root = Root;
			string fullpath = MakePath(root, topic);
			bool isNew = !(File.Exists(fullpath));

			// Get old topic so we can analyze it for properties to compare with the new one
			string oldText = null;
			Hashtable oldProperties = null;
			if (!isNew)
			{
				using (StreamReader sr = new StreamReader(new FileStream(fullpath, FileMode.Open, FileAccess.Read, FileShare.Read)))
				{
					oldText = sr.ReadToEnd();
				}
				oldProperties = ExtractExplicitFieldsFromTopicBody(oldText);	
			}

			// Change it
			using (StreamWriter sw = new StreamWriter(fullpath))
			{
				sw.Write(content);
			}

			// Quick check to see if we're about to let somebody write the DefinitionTopic for this ContentBase.  
			// If so, we reset our Info object to reread
			string pathToDefinitionTopic = MakePath(Root, DefinitionTopicName.LocalName);
			if (fullpath == pathToDefinitionTopic)
				this.Federation.InvalidateNamespace(Namespace);

			// Record changes
			try
			{
				AbsoluteTopicName absTopic = topic.AsAbsoluteTopicName(Namespace);

				gen.Push();

				// Record the topic-level change
				if (isNew)
					gen.RecordCreatedTopic(absTopic);
				else
					gen.RecordUpdatedTopic(absTopic);

				//	Now process the properties
				Hashtable newProperties = ExtractExplicitFieldsFromTopicBody(content);
				if (isNew)
				{
					foreach (string pName in newProperties.Keys)
						gen.RecordPropertyChange(absTopic, pName, FederationUpdate.PropertyChangeType.PropertyAdd);
					gen.RecordPropertyChange(absTopic, "_Body", FederationUpdate.PropertyChangeType.PropertyAdd);
					gen.RecordPropertyChange(absTopic, "_TopicName", FederationUpdate.PropertyChangeType.PropertyAdd);
					gen.RecordPropertyChange(absTopic, "_TopicFullName", FederationUpdate.PropertyChangeType.PropertyAdd);
					gen.RecordPropertyChange(absTopic, "_LastModifiedBy", FederationUpdate.PropertyChangeType.PropertyAdd);
					gen.RecordPropertyChange(absTopic, "_CreationTime", FederationUpdate.PropertyChangeType.PropertyAdd);
					gen.RecordPropertyChange(absTopic, "_ModificationTime", FederationUpdate.PropertyChangeType.PropertyAdd);
				}
				else
				{
					if (content != oldText)
					{
						FillFederationUpdateByComparingPropertyHashes(gen, absTopic, oldProperties, newProperties);
						gen.RecordPropertyChange(absTopic, "_Body", FederationUpdate.PropertyChangeType.PropertyUpdate);
					}
					gen.RecordPropertyChange(absTopic, "_ModificationTime", FederationUpdate.PropertyChangeType.PropertyUpdate);				
				}
			}
			finally
			{
				gen.Pop();
			}
		}
Example #4
0
		/// <summary>
		/// All of the FileInfos for the historical versions of a given topic
		/// </summary>
		/// <param name="topic"></param>
		/// <returns>FileInfos</returns>
		FileInfo[] FileInfosForTopic(LocalTopicName topic)
		{
			FileInfo [] answer = {};

			// If the topic does not exist, we ignore any historical versions (the result of a delete)
			if (!TipFileExists(topic.Name))
				return answer;

			try
			{
				answer = new DirectoryInfo(Root).GetFiles(topic.Name + "(*).awiki");
			}
			catch (DirectoryNotFoundException e)
			{
				System.Diagnostics.Debug.WriteLine(e.ToString()); 
			}
			return answer;
		}
Example #5
0
		/// <summary>
		/// Answer all of the versions for a given topic
		/// </summary>
		/// <remarks>
		/// TODO: Change this to return TopicChanges instead of the TopicNames
		/// </remarks>
		/// <param name="topic">A topic</param>
		/// <returns>Enumeration of the topic names (with non-null versions in them) </returns>
		override public IEnumerable AllVersionsForTopic(LocalTopicName topic)
		{
			ArrayList answer = new ArrayList();
			FileInfo[] infos = FileInfosForTopic(topic);
			ArrayList sortable = new ArrayList();
			foreach (FileInfo each in infos)
				sortable.Add(new FileInfoTopicData(each, Namespace));
			BackingTopic back = GetBackingTopicNamed(topic);
			if (back != null)
				sortable.Add(new BackingTopicTopicData(back));
			sortable.Sort(new TimeSort());
			foreach (TopicData each in sortable)
			{
				AbsoluteTopicName name = topic.AsAbsoluteTopicName(Namespace);
				name.Version = each.Version;
				answer.Add(name);
			}
			return answer;
		}
Example #6
0
		/// <summary>
		/// Answer a TextReader for the given topic
		/// </summary>
		/// <param name="topic"></param>
		/// <exception cref="TopicNotFoundException">Thrown when the topic doesn't exist</exception>
		/// <returns>TextReader</returns>
		override public TextReader TextReaderForTopic(LocalTopicName topic)
		{
			string topicFile = TopicPath(topic);
			if (topicFile == null || !File.Exists(topicFile))
			{
				BackingTopic back = GetBackingTopicNamed(topic);
				if (back != null)
					return new StringReader(back.Body);
				throw TopicNotFoundException.ForTopic(topic, Namespace);
			}
			return new StreamReader(new FileStream(topicFile, FileMode.Open, FileAccess.Read, FileShare.Read));
		}
Example #7
0
		/// <summary>
		/// Answer when a topic was created
		/// </summary>
		/// <param name="topic">The topic</param>
		/// <returns></returns>
		override public DateTime GetTopicCreationTime(LocalTopicName topic)
		{
			string path = TopicPath(topic);
			if (File.Exists(path))
				return File.GetCreationTime(path);
			BackingTopic back = GetBackingTopicNamed(topic);
			if (back != null)
				return back.CreationTime;
			throw TopicNotFoundException.ForTopic(topic, Namespace);			
		}
Example #8
0
		/// <summary>
		/// Answer true if a topic exists in this ContentBase
		/// </summary>
		/// <param name="name">Name of the topic</param>
		/// <returns>true if it exists</returns>
		override public bool TopicExistsLocally(LocalTopicName name)
		{
			if (BackingTopics.ContainsKey(name.Name))
				return true;
			return File.Exists(MakePath(Root, name));
		}
Example #9
0
		/// <summary>
		/// Delete a topic
		/// </summary>
		/// <param name="topic"></param>
		public abstract void DeleteTopic(LocalTopicName topic);
Example #10
0
		/// <summary>
		/// Answer a TextReader for the given topic
		/// </summary>
		/// <param name="topic"></param>
		/// <exception cref="TopicNotFoundException">Thrown when the topic doesn't exist</exception>
		/// <returns>TextReader</returns>
		public abstract TextReader TextReaderForTopic(LocalTopicName topic);
Example #11
0
		/// <summary>
		/// Change the value of a property (aka field) in a a topic.  If the topic doesn't exist, it will be created.
		/// </summary>
		/// <param name="topic">The topic whose property is to be changed</param>
		/// <param name="field">The name of the property to change</param>
		/// <param name="rep">The new value for the field</param>
		public void SetFieldValue(LocalTopicName topic, string field, string rep, bool writeNewVersion)
		{
			if (!TopicExistsLocally(topic))
			{
				WriteTopic(topic, "");
			}

			string original = Read(topic);

			// Multiline values need to end a complete line
			string repWithLineEnd = rep;
			if (!repWithLineEnd.EndsWith("\n"))
				repWithLineEnd = repWithLineEnd + "\n";
			bool newValueIsMultiline = rep.IndexOf("\n") > 0;

			string simpleField = "(?<name>(" + field + ")):(?<val>[^\\[].*)";
			string multiLineField = "(?<name>(" + field + ")):\\[(?<val>[^\\[]*\\])";

			string update = original;
			if (new Regex(simpleField).IsMatch(original))
			{
				if (newValueIsMultiline)
					update = Regex.Replace (original, simpleField, "${name}:[ " + repWithLineEnd + "]");
				else
					update = Regex.Replace (original, simpleField, "${name}: " + rep);
			}
			else if (new Regex(multiLineField).IsMatch(original))
			{
				if (newValueIsMultiline)
					update = Regex.Replace (original, multiLineField, "${name}:[ " + repWithLineEnd + "]");
				else
					update = Regex.Replace (original, multiLineField, "${name}: " + rep);
			}
			else
			{
				if (!update.EndsWith("\n"))
					update = update + "\n";
				if (rep.IndexOf("\n") == -1)
					update += field + ": " + repWithLineEnd;
				else
					update += field + ":[ " + repWithLineEnd + "]\n";
			}
			if (writeNewVersion)
				WriteTopicAndNewVersion(topic, update);
			else
				WriteTopic(topic, update);
		}
Example #12
0
		/// <summary>
		/// Reach and answer all the properties (aka fields) for the given topic.  This includes both the 
		/// properties defined in the topic plus the extra properties that every topic has (e.g., _TopicName, _TopicFullName, _LastModifiedBy, etc.)
		/// </summary>
		/// <param name="topic"></param>
		/// <returns>Hashtable (keys = string property names, values = values [as strings]);  or null if the topic doesn't exist</returns>
		public Hashtable GetFieldsForTopic(LocalTopicName topic)
		{
			if (!TopicExistsLocally(topic))
				return null;

			string allLines = Read(topic);
			Hashtable answer = ExtractExplicitFieldsFromTopicBody(allLines);	
			Federation.AddImplicitPropertiesToHash(answer, topic.AsAbsoluteTopicName(Namespace), GetTopicLastAuthor(topic), GetTopicCreationTime(topic), GetTopicLastWriteTime(topic), allLines);
			return answer;
		}
Example #13
0
		/// <summary>
		/// Answer the contents of a given topic
		/// </summary>
		/// <param name="topic">The topic</param>
		/// <returns>The contents of the topic or null if it can't be read (e.g., doesn't exist)</returns>
		public string Read(LocalTopicName topic)
		{
			using (TextReader st = TextReaderForTopic(topic))
			{
				if (st == null)
					return null;
				return st.ReadToEnd();
			}
		}
Example #14
0
		/// <summary>
		/// Answer the identify of the author who last modified a given topic
		/// </summary>
		/// <param name="topic"></param>
		/// <returns>a user name</returns>
		public abstract string GetTopicLastAuthor(LocalTopicName topic);
Example #15
0
		/// <summary>
		/// Answer when a topic was created
		/// </summary>
		/// <param name="topic">The topic</param>
		/// <returns></returns>
		public abstract DateTime GetTopicCreationTime(LocalTopicName topic);
Example #16
0
		string TopicPath(LocalTopicName localTopicName)
		{
			return MakePath(Root, localTopicName);
		}
Example #17
0
		/// <summary>
		/// Answer the full file system path for a given topic in a given folder.  
		/// </summary>
		/// <param name="root">File system path to the root directory for the containing content base</param>
		/// <param name="name">The name of the topic</param>
		/// <returns>Full path to the file containing the content for the most recent version of the topic</returns>
		static string MakePath(string root, LocalTopicName name)
		{
			if (name.Version == null || name.Version.Length == 0)
				return root + "\\" + name.Name + ".wiki";
			else
				return root + "\\" + name.Name + "(" + name.Version + ").awiki";
		}
Example #18
0
		/// <summary>
		/// Answer all of the versions for a given topic
		/// </summary>
		/// <remarks>
		/// TODO: Change this to return TopicChanges instead of the TopicNames
		/// </remarks>
		/// <param name="topic">A topic</param>
		/// <returns>Enumeration of the topic names (with non-null versions in them) </returns>
		public abstract IEnumerable AllVersionsForTopic(LocalTopicName topic);
Example #19
0
		/// <summary>
		/// Answer whether a topic exists and is writable
		/// </summary>
		/// <param name="topic">The topic (must directly be in this content base)</param>
		/// <returns>true if the topic exists AND is writable by the current user; else false</returns>
		override public bool IsExistingTopicWritable(LocalTopicName topic)
		{

			string path = TopicPath(topic);
			if (!File.Exists(path))
			{
				BackingTopic back = GetBackingTopicNamed(topic);
				if (back == null)
					return false;
				return back.CanOverride;
			}
			DateTime old = File.GetLastWriteTimeUtc(path);
			try
			{
				// Hacky implementation, but there's no better with the framework to do this that just to try and see what happens!!!
				FileStream stream = File.OpenWrite(path);
				stream.Close();
			}
			catch (UnauthorizedAccessException unauth)
			{
				unauth.ToString();
				return false;
			}
			File.SetLastWriteTimeUtc(path, old);
			return true;
		}
Example #20
0
		/// <summary>
		/// Returns the most recent version for the given topic
		/// </summary>
		/// <param name="topic">The topic</param>
		/// <returns>The most recent version string for the topic</returns>
		public abstract string LatestVersionForTopic(LocalTopicName topic);
Example #21
0
		/// <summary>
		/// Answer the identify of the author who last modified a given topic
		/// </summary>
		/// <param name="topic"></param>
		/// <returns>a user name</returns>
		override public string GetTopicLastAuthor(LocalTopicName topic)
		{

			FileInfo[] infos = FileInfosForTopic(topic);
			if (infos.Length == 0)
			{
				BackingTopic back = GetBackingTopicNamed(topic);
				if (back != null)
					return back.LastAuthor;
				return AnonymousUserName;
			}

			ArrayList all = new ArrayList();
			foreach (FileInfo each in infos)
				all.Add(new FileInfoTopicData(each, Namespace));
			all.Sort(new TimeSort());
			TopicData info = (TopicData)(all[0]);
			string auth = info.Author;
			if (auth == null)
				return AnonymousUserName;
			return auth;
		}
Example #22
0
		/// <summary>
		/// A list of TopicChanges to a topic since a given date [sorted by date]
		/// </summary>
		/// <param name="topic">A given date</param>
		/// <param name="stamp">A non-null timestamp; changes before this time won't be included in the answer </param>
		/// <param name="rule">A composite cache rule to fill with rules that represented accumulated dependencies (or null)</param>
		/// <returns>Enumeration of TopicChanges</returns>
		public abstract IEnumerable AllChangesForTopicSince(LocalTopicName topic, DateTime stamp, CompositeCacheRule rule);
Example #23
0
		/// <summary>
		/// Delete a topic
		/// </summary>
		/// <param name="topic"></param>
		override public void DeleteTopic(LocalTopicName topic)
		{
			string path = TopicPath(topic);
			if (!File.Exists(path))
				return;

			// Delete the sucker!
			File.Delete(path);

			// Fire the event
			FederationUpdate update = new FederationUpdate();
			update.RecordDeletedTopic(topic.AsAbsoluteTopicName(Namespace));
			OnFederationUpdated(new FederationUpdateEventArgs(update));
		}
Example #24
0
		/// <summary>
		/// A list of TopicChanges to a topic since a given date [sorted by date]
		/// </summary>
		/// <param name="topic">A given date</param>
		/// <param name="stamp">A non-null timestamp; changes before this time won't be included in the answer </param>
		/// <returns>Enumeration of TopicChanges</returns>
		public IEnumerable AllChangesForTopicSince(LocalTopicName topic, DateTime stamp)
		{
			return AllChangesForTopicSince(topic, stamp, null);
		}
Example #25
0
		/// <summary>
		/// Returns the most recent version for the given topic
		/// </summary>
		/// <param name="topic">The topic</param>
		/// <returns>The most recent version string for the topic</returns>
		override public string LatestVersionForTopic(LocalTopicName topic)
		{				
			ArrayList sortable = new ArrayList();

			FileInfo [] infos = FileInfosForTopic(topic);
			foreach (FileInfo each in infos)
				sortable.Add(new FileInfoTopicData(each, Namespace));
			BackingTopic back = GetBackingTopicNamed(topic);
			if (back != null)
				sortable.Add(new BackingTopicTopicData(back));
			
			if (sortable.Count == 0)
				return null;
			sortable.Sort(new TimeSort());

			return ((TopicData)(sortable[0])).Version; 
		}
Example #26
0
		/// <summary>
		/// A list of TopicChanges to a topic (sorted by date)
		/// </summary>
		/// <param name="topic">The topic</param>
		/// <returns>Enumeration of TopicChanges </returns>
		public IEnumerable AllChangesForTopic(LocalTopicName topic)
		{
			return AllChangesForTopicSince(topic, DateTime.MinValue, null);
		}
Example #27
0
		/// <summary>
		/// A list of TopicChanges to a topic since a given date [sorted by date]
		/// </summary>
		/// <param name="topic">A given date</param>
		/// <param name="stamp">A non-null timestamp; changes before this time won't be included in the answer </param>
		/// <returns>Enumeration of TopicChanges</returns>
		override public IEnumerable AllChangesForTopicSince(LocalTopicName topic, DateTime stamp, CompositeCacheRule rule)
		{
			ArrayList answer = new ArrayList();
			FileInfo[] infos = FileInfosForTopic(topic);
			ArrayList sortable = new ArrayList();
			foreach (FileInfo each in infos)
				sortable.Add(new FileInfoTopicData(each, Namespace));
			BackingTopic back = GetBackingTopicNamed(topic);
			if (back != null)
				sortable.Add(new BackingTopicTopicData(back));
			sortable.Sort(new TimeSort());
			TopicsCacheRule tcr = null;
			if (rule != null)
			{
				tcr = new TopicsCacheRule(Federation);
				tcr.AddTopic(topic.AsAbsoluteTopicName(Namespace));
				rule.Add(tcr);
			}
			foreach (TopicData each in sortable)
			{
				if (each.LastModificationTime < stamp)
					continue;
				AbsoluteTopicName name = topic.AsAbsoluteTopicName(Namespace);
				name.Version = each.Version;
				TopicChange change = TopicChangeFromName(name);
				answer.Add(change);
				if (tcr != null)
					tcr.AddTopic(name.AsAbsoluteTopicName(Namespace));
			}
			return answer;
		}
Example #28
0
		public IEnumerable AllChangesForTopic(LocalTopicName topic, CompositeCacheRule rule)
		{
			return AllChangesForTopicSince(topic, DateTime.MinValue, rule);
		}
Example #29
0
		/// <summary>
		/// Rename the given topic.  If requested, find references and fix them up.  Answer a report of what was fixed up.  Throw a DuplicationTopicException
		/// if the new name is the name of a topic that already exists.
		/// </summary>
		/// <param name="oldName">Old topic name</param>
		/// <param name="newName">The new name</param>
		/// <param name="fixup">true to fixup referenced topic *in this namespace*; false to do no fixups</param>
		/// <returns>ArrayList of strings that can be reported back to the user of what happened during the fixup process</returns>
		override public ArrayList RenameTopic(LocalTopicName oldName, string newName, bool fixup)
		{
			FederationUpdateGenerator gen = CreateFederationUpdateGenerator();

			// TRIGGER
			ArrayList answer = new ArrayList();
			string root = Root;
			string pathToTopicFile =  root + "\\" + oldName.Name + ".wiki";
			string pathToArchiveFolder = root + "\\archive\\" + oldName.Name;
			string newNameForTopicFile = root + "\\" + newName + ".wiki";
			string newNameForArchiveFolder = root + "\\archive\\" + newName;
			AbsoluteTopicName newFullName = new AbsoluteTopicName(newName, Namespace);

			// Make sure it's not goign to overwrite an existing topic
			if (TopicExistsLocally(newName))
			{
				throw DuplicateTopicException.ForTopic(newFullName);
			}

			// If the topic does not exist (e.g., it's a backing topic), don't bother...
			if (!TipFileExists(oldName.Name))
			{
				answer.Add("This topic can not be renamed (it is probably a backing topic).");
				return answer;
			}

			try
			{
				gen.Push();

				// Rename the archive files, too
				foreach (FileInfo each in FileInfosForTopic(oldName))
				{
					AbsoluteTopicName newNameForThisVersion = new AbsoluteTopicName(newName, Namespace);
					newNameForThisVersion.Version = ExtractVersionFromHistoricalFilename(each.Name);

					AbsoluteTopicName oldNameForThisVersion = new AbsoluteTopicName(oldName.Name, Namespace);
					oldNameForThisVersion.Version = newNameForThisVersion.Version;
					
					string newFilename = MakePath(root, newNameForThisVersion.LocalName);
					File.Move(each.FullName, newFilename);

					// record changes (a delete for the old one and an add for the new one)
					gen.RecordCreatedTopic(newNameForThisVersion);
					gen.RecordDeletedTopic(oldNameForThisVersion);
				}

				// Rename the topic file
				File.Move(pathToTopicFile, newNameForTopicFile);

				// Record changes (a delete for the old one and an add for the new one)
				gen.RecordCreatedTopic(newFullName);
				gen.RecordDeletedTopic(oldName.AsAbsoluteTopicName(Namespace));

				// Now get ready to do fixups
				if (!fixup)
					return answer;

				// OK, we need to do the hard work
				AbsoluteTopicName oldabs = oldName.AsAbsoluteTopicName(Namespace);
				AbsoluteTopicName newabs = new AbsoluteTopicName(newName, oldabs.Namespace);
				
				// Now the master loop
				foreach (AbsoluteTopicName topic in AllTopics(false))
					if (RenameTopicReferences(topic.LocalName, oldabs, newabs, gen))
						answer.Add("Found and replaced references in " + topic);
			}
			finally
			{
				gen.Pop();
			}

			return answer;
		}
Example #30
0
		/// <summary>
		/// Find the version of a topic immediately previous to another version
		/// </summary>
		/// <param name="topic">The name (with version) of the topic for which you want the change previous to</param>
		/// <returns>TopicChange or null if none</returns>
		public AbsoluteTopicName VersionPreviousTo(LocalTopicName topic)
		{
			bool next = false;
			bool first = true;
			AbsoluteTopicName answer = topic.AsAbsoluteTopicName(Namespace);
			foreach (TopicChange ver in AllChangesForTopic(topic))
			{
				answer.Version = ver.Version;
				if (next)
					return answer;
				if (topic.Version == null && !first)	// The version prior to the most recent is the second in line
					return answer;
				if (ver.Version == topic.Version)
					next = true;
				first = false;
			}
			return null;
		}