/// <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> /// 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(); } }
protected void FillFederationUpdateByComparingPropertyHashes(FederationUpdateGenerator batch, AbsoluteTopicName topic, Hashtable oldProps, Hashtable newProps) { // Loop through the old ones to find any that are changed or removed in the new foreach (DictionaryEntry e in oldProps) { if (newProps.ContainsKey(e.Key)) { object newValue = newProps[e.Key]; if (newValue.ToString() != e.Value.ToString()) { batch.RecordPropertyChange(topic, e.Key.ToString(), FederationUpdate.PropertyChangeType.PropertyUpdate); } } else batch.RecordPropertyChange(topic, e.Key.ToString(), FederationUpdate.PropertyChangeType.PropertyRemove); } // And also find the added ones by identifying those that are in the new set, but not the old foreach (DictionaryEntry e in newProps) { if (!oldProps.ContainsKey(e.Key)) batch.RecordPropertyChange(topic, e.Key.ToString(), FederationUpdate.PropertyChangeType.PropertyAdd); } }
/// <summary> /// Rename references (in a given topic) from one topic to a new name /// </summary> /// <param name="topicToLookIn"></param> /// <param name="oldName"></param> /// <param name="newName"></param> /// <returns></returns> public bool RenameTopicReferences(LocalTopicName topicToLookIn, AbsoluteTopicName oldName, AbsoluteTopicName newName, FederationUpdateGenerator gen) { string current = Read(topicToLookIn); MatchCollection wikiNames = Formatter.extractWikiNames.Matches(current); ArrayList processed = new ArrayList(); bool any = false; foreach (Match m in wikiNames) { string each = m.Groups["topic"].ToString(); if (processed.Contains(each)) { continue; // skip dup } processed.Add(each); RelativeTopicName relName = new RelativeTopicName(TopicName.StripEscapes(each)); // See if this is the old name. The only way it can be is if it's unqualified or if it's qualified with the current namespace. bool hit = (relName.Name == oldName.Name) && (relName.Namespace == null || relName.Namespace == oldName.Namespace); if (!hit) continue; // Now see if we got any hits or not string rep = Formatter.beforeWikiName + "(" + Formatter.RegexEscapeTopic(each) + ")" + Formatter.afterWikiName; // if the reference was fully qualified, retain that form in the new reference string replacementName = each.IndexOf(".") > -1 ? newName.Fullname : newName.Name; current = Regex.Replace(current, rep, "${before}" + replacementName + "${after}"); any = true; } if (any) { WriteTopicAndNewVersion(topicToLookIn, current, gen); } return any; }
/// <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> protected abstract void WriteTopic(LocalTopicName topic, string content, FederationUpdateGenerator gen);
/// <summary> /// Create a new FederationUpdateGenerator and hook its GenerationComplete event so that when that fires, this ContentBase fires its FederationUpdate event /// </summary> protected FederationUpdateGenerator CreateFederationUpdateGenerator() { FederationUpdateGenerator answer = new FederationUpdateGenerator(); answer.GenerationComplete += new FederationUpdateGenerator.GenerationCompleteEventHandler(FederationUpdateGeneratorGenerationComplete); 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> /// 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="gen">Object to recieve change info about the topic</param> protected override void WriteTopic(LocalTopicName topic, string content, FederationUpdateGenerator gen) { string topicName = MakeTopicName(topic); bool isNew = !(SqlHelper.TopicExists(Namespace, topicName, _ConnectionString)); // Get old topic so we can analyze it for properties to compare with the new one string oldText = null; Hashtable oldProperties = null; if (!isNew) { oldText = SqlHelper.GetTopicBody(Namespace, topicName, _ConnectionString); oldProperties = ExtractExplicitFieldsFromTopicBody(oldText); } SqlHelper.WriteTopic(Namespace, topicName, LastWriteTime(topic.NameWithVersion) ,_ConnectionString, content, ((topic.Version != null && topic.Version.Length > 0)?true:false)); // 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 if (topicName == ContentBase.DefinitionTopicLocalName) this.Federation.InvalidateNamespace(Namespace); // Record changes RecordTopicChanges(topic, gen, isNew, content, oldText, oldProperties); }
protected override void WriteTopic(LocalTopicName topic, string content, FederationUpdateGenerator gen) { throw ReadOnlyException(); }