/// <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(); } }
/// <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; }
/// <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; }
/// <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; }
/// <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; }
/// <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)); }
/// <summary> /// Write a topic (and create a historical version) /// </summary> /// <param name="topic">The topic to write</param> /// <param name="content">The content</param> void WriteTopicAndNewVersion(LocalTopicName topic, string content, FederationUpdateGenerator gen) { gen.Push(); LocalTopicName versionless = new LocalTopicName(topic.Name); bool isVersionlessNew = !TopicExistsLocally(versionless); bool isVersionedNew = !TopicExistsLocally(topic); string oldAuthor = null; if (!isVersionlessNew) oldAuthor = GetTopicLastAuthor(topic); // Write it WriteTopic(versionless, content, gen); WriteTopic(topic, content, gen); //Generate author property change if needed if (!isVersionlessNew) { //See if the last modified by has changed (not this only happens when writing out the versioned tip file) string newLastAuthor = GetTopicLastAuthor(topic); if (oldAuthor != newLastAuthor) { gen.RecordPropertyChange(versionless.AsAbsoluteTopicName(Namespace), "_LastModifiedBy", FederationUpdate.PropertyChangeType.PropertyUpdate); } } gen.Pop(); }
/// <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; }
/// <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> public override ArrayList RenameTopic(LocalTopicName oldName, string newName, bool fixup) { FederationUpdateGenerator gen = CreateFederationUpdateGenerator(); // TRIGGER ArrayList answer = new ArrayList(); string currentTopicName = oldName.Name; string newTopicName = 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 (!TipTopicRecordExists(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 (SqlInfoForTopic each in SqlTopicInfosForTopic(oldName)) { AbsoluteTopicName newNameForThisVersion = new AbsoluteTopicName(newName, Namespace); newNameForThisVersion.Version = ExtractVersionFromTopicName(each.Name); AbsoluteTopicName oldNameForThisVersion = new AbsoluteTopicName(oldName.Name, Namespace); oldNameForThisVersion.Version = newNameForThisVersion.Version; SqlHelper.RenameTopic(Namespace, each.Name, MakeTopicName(newNameForThisVersion.LocalName), _ConnectionString); // 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 SqlHelper.RenameTopic(Namespace, currentTopicName, newTopicName, _ConnectionString); // 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; }
private void RecordTopicChanges(LocalTopicName topic, FederationUpdateGenerator gen, bool isNew, string content, string oldText, Hashtable oldProperties) { 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(); } }
/// <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 override IEnumerable AllChangesForTopicSince(LocalTopicName topic, DateTime stamp, CompositeCacheRule rule) { ArrayList answer = new ArrayList(); SqlInfoForTopic[] infos = SqlHelper.GetSqlTopicInfosForTopicSince(Namespace, topic.Name, stamp, ConnectionString); ArrayList sortable = new ArrayList(); foreach (SqlInfoForTopic each in infos) sortable.Add(new SqlInfoTopicData(each, Namespace)); BackingTopic back = GetBackingTopicNamed(topic); bool sortAgain = false; if (back != null) { sortAgain = true; sortable.Add(new BackingTopicTopicData(back)); } if( sortAgain ) 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; }
/// <summary> /// Delete a topic /// </summary> /// <param name="topic"></param> public override void DeleteTopic(LocalTopicName topic) { if( !SqlHelper.TopicExists(Namespace, topic.Name, _ConnectionString) ) { return; } SqlHelper.DeleteTopic(Namespace, topic.Name, _ConnectionString); // Fire the event FederationUpdate update = new FederationUpdate(); update.RecordDeletedTopic(topic.AsAbsoluteTopicName(Namespace)); OnFederationUpdated(new FederationUpdateEventArgs(update)); }
public override IEnumerable AllVersionsForTopic(LocalTopicName topic) { ArrayList answer = new ArrayList(); AbsoluteTopicName only = topic.AsAbsoluteTopicName(Namespace); only.Version = VersionConst; answer.Add(only); return answer; }