/// <summary> /// Given the prefix of a page, asynchronously search for a list of titles for auto completion. /// </summary> /// <remarks>Do not modify the returned list.</remarks> public async Task <IList <OpenSearchResultEntry> > GetAutoCompletionItemsAsync(string expression, int defaultNamespace = BuiltInNamespaces.Main) { if (expression == null) { throw new ArgumentNullException(nameof(expression)); } string normalizedExpression; try { normalizedExpression = WikiLink.NormalizeWikiLink(await GetSiteAsync(), expression); } catch (ArgumentException) { normalizedExpression = expression; } var tp = Tuple.Create(normalizedExpression, defaultNamespace); var cached = _AutoCompletionItemsCache.TryGetValue(tp); if (cached != null && DateTime.Now - cached.Item2 < _CacheExpiry) { return(cached.Item1); } var site = await GetSiteAsync(); var entries = await site.OpenSearchAsync(expression, 20, defaultNamespace, OpenSearchOptions.None); _AutoCompletionItemsCache[tp] = Tuple.Create(entries, DateTime.Now); return(entries); }
/// <summary> /// Imports the Lua module with the specified name and evaluates the specified Lua code with it. /// </summary> /// <typeparam name="T">The expected evaluation return value type.</typeparam> /// <param name="site">The MediaWiki site on which to evaluate the module.</param> /// <param name="moduleName">Name of the module to be imported, with or without <c>Module:</c> prefix.</param> /// <param name="epilog">The Lua code snippet used to return value from the imported module (denoted as <c>p</c> in Lua), /// or <c>null</c> to use default epilog (<c>return p</c>).</param> /// <param name="serializer">The JsonSerializer used to deserialize the return value from JSON, or <c>null</c> to use default JSON serializer.</param> /// <param name="cancellationToken">A token used to cancel the operation.</param> /// <returns>The deserialized Lua evaluation result.</returns> public static Task <T> ScribuntoLoadDataAsync <T>(this WikiSite site, string moduleName, string?epilog, JsonSerializer?serializer, CancellationToken cancellationToken) { if (site == null) { throw new ArgumentNullException(nameof(site)); } if (string.IsNullOrEmpty(moduleName)) { throw new ArgumentException(Prompts.ExceptionArgumentNullOrEmpty, nameof(moduleName)); } cancellationToken.ThrowIfCancellationRequested(); var moduleLink = WikiLink.Parse(site, moduleName); var normalizedModuleName = moduleLink.FullTitle; if (string.IsNullOrEmpty(moduleLink.NamespaceName)) { normalizedModuleName = "Module:" + normalizedModuleName; } if (epilog == null) { epilog = "return p"; } var sb = new StringBuilder("-- ScribuntoLoadDataAsync\n\n", 64 + normalizedModuleName.Length + epilog.Length); sb.Append("local p = require([==["); sb.Append(normalizedModuleName); sb.Append("]==])\n\n"); sb.Append(epilog); sb.AppendLine(); return(ScribuntoExecuteLuaAsync <T>(site, sb.ToString(), serializer, cancellationToken)); }
public async Task InterwikiLinkTests() { // We will not login onto any site… var originSite = await Family.GetSiteAsync("test2"); // With originating WikiSite var link = await WikiLink.ParseAsync(originSite, Family, "WikiPedia:SANDBOX"); AssertWikiLink(link, null, "Wikipedia", "SANDBOX"); link = await WikiLink.ParseAsync(originSite, Family, "FR___:_ __Wp__ _: SANDBOX"); AssertWikiLink(link, "fr", "Wikipédia", "SANDBOX"); link = await WikiLink.ParseAsync(originSite, Family, "EN:fr: LZH:Project:SANDBOX"); AssertWikiLink(link, "lzh", "維基大典", "SANDBOX"); // We don't have de in WikiFamily, but WP has de in its inter-wiki table. // Should works as if we haven't specified Family. link = await WikiLink.ParseAsync(originSite, Family, "de:Project:SANDBOX"); AssertWikiLink(link, "de", null, "Project:SANDBOX"); // Without originating WikiSite await Assert.ThrowsAsync <ArgumentException>(() => WikiLink.ParseAsync(Family, "WikiPedia:SANDBOX")); link = await WikiLink.ParseAsync(Family, "FR___:_ __Wp__ _: SANDBOX"); AssertWikiLink(link, "fr", "Wikipédia", "SANDBOX"); link = await WikiLink.ParseAsync(Family, "EN:fr: LZH:Project:SANDBOX"); AssertWikiLink(link, "lzh", "維基大典", "SANDBOX"); await Assert.ThrowsAsync <ArgumentException>(() => WikiLink.ParseAsync(Family, "unk:WikiPedia:SANDBOX")); }
/// <summary> /// Initializes a new instance of <see cref="Board"/> from site and page title. /// </summary> /// <param name="site">The Wikia site.</param> /// <param name="title">Full page title of the board.</param> /// <param name="defaultNamespaceId">The default namespace ID to be used for the <paramref name="title"/>.</param> /// <exception cref="ArgumentNullException"><paramref name="site"/> or <paramref name="title"/> is <c>null</c>.</exception> public Board(WikiaSite site, string title, int defaultNamespaceId) { Site = site ?? throw new ArgumentNullException(nameof(site)); var link = WikiLink.Parse(site, title, defaultNamespaceId); Page = new WikiPageStub(link.FullTitle, link.Namespace.Id); }
/// <summary> /// Asynchronously uploads a file in this title. /// </summary> /// <param name="site"></param> /// <param name="title"></param> /// <param name="source">Source of the file.</param> /// <param name="comment">Comment of the upload, as well as the page content if it doesn't exist.</param> /// <param name="ignoreWarnings">Ignore any warnings. This must be set to upload a new version of an existing image.</param> /// <param name="watch">Whether to add the file into your watchlist.</param> /// <param name="cancellationToken">The cancellation token that will be checked prior to completing the returned task.</param> /// <exception cref="UnauthorizedAccessException">You do not have the permission to upload the file.</exception> /// <exception cref="OperationFailedException"> /// There's an general failure while uploading the file. /// - or - /// Since MW 1.31, if you are uploading the exactly same content to the same title /// with <paramref name="ignoreWarnings"/> set to <c>true</c>, /// you will reveive this exception with <see cref="OperationFailedException.ErrorCode"/> /// set to <c>fileexists-no-change</c>. See https://gerrit.wikimedia.org/r/378702 . /// </exception> /// <exception cref="TimeoutException">Timeout specified in <see cref="WikiClient.Timeout"/> has been reached.</exception> /// <returns>An <see cref="UploadResult"/>. You need to check <see cref="UploadResult.ResultCode"/> for further action.</returns> public static async Task <UploadResult> UploadAsync(this WikiSite site, string title, WikiUploadSource source, string comment, bool ignoreWarnings, AutoWatchBehavior watch, CancellationToken cancellationToken) { if (source == null) { throw new ArgumentNullException(nameof(source)); } Debug.Assert(source != null); var link = WikiLink.Parse(site, title, BuiltInNamespaces.File); using (site.BeginActionScope(null, title, source)) { var requestFields = new Dictionary <string, object> { { "action", "upload" }, { "watchlist", watch }, { "token", WikiSiteToken.Edit }, { "filename", link.Title }, { "comment", comment }, { "ignorewarnings", ignoreWarnings }, }; foreach (var p in source.GetUploadParameters(site.SiteInfo)) { requestFields[p.Key] = p.Value; } var request = new MediaWikiFormRequestMessage(requestFields, true); site.Logger.LogDebug("Start uploading."); var jresult = await site.InvokeMediaWikiApiAsync(request, cancellationToken); var result = jresult["upload"].ToObject <UploadResult>(Utility.WikiJsonSerializer); site.Logger.LogInformation("Uploaded. Result={Result}.", result.ResultCode); return(result); } }
public async Task WikiLinkTest1() { var WpTestSite = await WpTest2SiteAsync; var link1 = WikiLink.Parse(WpTestSite, "____proJEct__talk_:___sandbox_"); var link2 = WikiLink.Parse(WpTestSite, "__ _pROject_ _talk_:___sandbox_", BuiltInNamespaces.Category); var link3 = WikiLink.Parse(WpTestSite, "___sandbox_ test__", BuiltInNamespaces.Category); var link4 = WikiLink.Parse(WpTestSite, "__: sandbox test ", BuiltInNamespaces.Template); var link5 = WikiLink.Parse(WpTestSite, "___lZh__:project:test|", BuiltInNamespaces.Template); Assert.Equal("Wikipedia talk:Sandbox", link1.ToString()); Assert.Equal("Wikipedia talk", link1.NamespaceName); Assert.Equal("Sandbox", link1.Title); Assert.Equal("Wikipedia talk:Sandbox", link1.Target); Assert.Equal("Wikipedia talk:Sandbox", link1.DisplayText); Assert.Equal("https://test2.wikipedia.org/wiki/Wikipedia%20talk:Sandbox", link1.TargetUrl); Assert.Null(link1.InterwikiPrefix); Assert.Null(link1.Section); Assert.Null(link1.Anchor); Assert.Equal("Wikipedia talk:Sandbox", link2.ToString()); Assert.Equal("Category:Sandbox test", link3.ToString()); Assert.Equal("Sandbox test", link4.ToString()); Assert.Equal("lzh:Project:test|", link5.ToString()); Assert.Equal("Project:test", link5.DisplayText); Assert.Equal("lzh", link5.InterwikiPrefix); Assert.Equal("lzh:Project:test", link5.Target); Assert.Equal("", link5.Anchor); var link6 = WikiLink.Parse(WpTestSite, "sandbox#sect|anchor", BuiltInNamespaces.Template); Assert.Equal("Template:Sandbox#sect|anchor", link6.ToString()); Assert.Equal("Template:Sandbox#sect", link6.Target); Assert.Equal("sect", link6.Section); Assert.Equal("anchor", link6.Anchor); Assert.Equal("anchor", link6.DisplayText); }
public async Task RunAsync() { var apg = new AllPagesGenerator(Site) { NamespaceId = Site.Namespaces["Item"].Id, PaginationSize = 100 }; using (var writer = File.CreateText(FileName)) using (var ie = apg.EnumItemsAsync().Buffer(100).GetEnumerator()) { while (await ie.MoveNext()) { var entities = ie.Current.Select(s => new Entity(Site, WikiLink.Parse(Site, s.Title).Title)).ToList(); await entities.RefreshAsync(EntityQueryOptions.FetchLabels, new[] { "en", "zh", "zh-cn", "zh-hans" }); foreach (var entity in entities) { writer.Write(entity.Id); writer.Write('\t'); writer.Write(entity.Labels["en"]); writer.Write('\t'); writer.Write(entity.Labels["zh-cn"] ?? entity.Labels["zh-hans"] ?? entity.Labels["zh"]); writer.WriteLine(); } } } }
protected void ReloadPageContent() { // Switch content model, if necessary. var newContentModel = WikiPage.ContentModel ?? MediaWikiUtility.InferContentModelFromTitle( WikiLink.Parse(WikiPage.Site, WikiPage.Title)); if (EditorContentModel != newContentModel) { var ls = _SettingsService.GetSettingsByWikiContentModel(newContentModel); if (string.IsNullOrEmpty(ls.LanguageName)) { TextEditor = null; } else { TextEditor = _TextEditorFactory.CreateTextEditor(ls.LanguageName, true); TextEditor.WikiSite = SiteContext; TextEditor.DocumentOutline = DocumentOutline; } EditorContentModel = newContentModel; } if (TextEditor != null) { TextEditor.TextBox.Text = WikiPage.Content; TextEditor.InvalidateDocumentOutline(true); } }
private void CheckNode(WikiLink link, DiagnosticEmitter e) { if (string.IsNullOrWhiteSpace(link.Target?.ToString())) { e.EmptyWikilinkTarget(link.ToRange()); } }
/// <summary> /// Initializes a new <see cref="Topic"/> instance from MW site and topic page title. /// </summary> /// <param name="site">MediaWiki site.</param> /// <param name="title">Either full page title of the Flow discussion board including <c>Topic:</c> namespace prefix, or the workflow ID of the board.</param> /// <exception cref="ArgumentNullException"><paramref name="site"/> or <paramref name="title"/> is <c>null</c>.</exception> /// <exception cref="ArgumentException"><paramref name="title"/> is not a valid title.</exception> public Topic(WikiSite site, string title) { Site = site ?? throw new ArgumentNullException(nameof(site)); var link = WikiLink.Parse(site, title, FlowNamespaces.Topic); Title = link.ToString(); WorkflowId = link.Title.ToLowerInvariant(); }
public async Task TestMethod2() { var WikiaTestSite = await WikiaTestSiteAsync; var link1 = WikiLink.Parse(WikiaTestSite, "__ _project_ _talk_:___sandbox_", BuiltInNamespaces.Category); var link2 = WikiLink.Parse(WikiaTestSite, "part1:part2:part3", BuiltInNamespaces.Category); Assert.Equal("Mediawiki 1.19 test Wiki talk:Sandbox", link1.ToString()); Assert.Equal("Category:Part1:part2:part3", link2.ToString()); }
public BoardHeader(WikiSite site, string boardTitle) { if (site == null) { throw new ArgumentNullException(nameof(site)); } Site = site; BoardTitle = WikiLink.NormalizeWikiLink(site, boardTitle); }
private string CreateHtmlLink(PagePath parsedPage, WikiLink link) { if (link.Exists) { return string.Format(@"<a href=""{0}"" class=""wiki-link"">{1}</a>", parsedPage.GetPathRelativeTo(link.PagePath), link.Title); } return string.Format( @"<a href=""{0}?title={1}"" class=""wiki-link missing"">{2}</a>", parsedPage.GetPathRelativeTo(link.PagePath), link.Title, link.Title); }
public static string InferContentModel(this Page page) { if (page == null) { throw new ArgumentNullException(nameof(page)); } if (page.ContentModel != null) { return(page.ContentModel); } // For older MediaWiki sites... return(InferContentModelFromTitle(WikiLink.Parse(page.Site, page.Title))); }
private void linkList_MouseDoubleClick(object sender, MouseEventArgs e) { if (linkList.SelectedItem == null) { return; } WikiLink link = (WikiLink)linkList.SelectedItem; titleBox.Text = link.DestPageURL; FetchPage(link.DestPageURL); }
private WikiLink ParseWikiLink() { // Note that wikilink cannot nest itself. ParseStart(@"\||\n|\[\[|\]\]", true); if (ConsumeToken(@"\[\[") == null) { return(ParseFailed <WikiLink>()); } var target = new Run(); if (!ParseRun(RunParsingMode.ExpandableText, target, true)) { if (options.AllowEmptyWikiLinkTarget) { target = null; } else { return(ParseFailed <WikiLink>()); } } var node = new WikiLink { Target = target }; if (ConsumeToken(@"\|") != null) { var text = new Run(); // Text accepts pipe CurrentContext.Terminator = Terminator.Get(@"\n|\[\[|\]\]"); // For [[target|]], Text == Empty Run // For [[target]], Text == null if (ParseRun(RunParsingMode.ExpandableText, text, true)) { node.Text = text; } } if (ConsumeToken(@"\]\]") == null) { if (options.AllowClosingMarkInference) { node.SetInferredClosingMark(); } else { return(ParseFailed <WikiLink>()); } } return(ParseSuccessful(node)); }
public void WikiLinkTest() { var value = new WikiLink(false, false, false, false, "Test Title", "Test Namespace"); var json = JsonSerializer.Serialize(value); Console.WriteLine(); Console.WriteLine(json); var deserialized = JsonSerializer.Deserialize <WikiLink>(json); Assert.AreEqual(value, deserialized); Assert.AreEqual(json, JsonSerializer.Serialize(deserialized)); value = new WikiLink(false, true, false, false, "Test Title", "Test Namespace"); json = JsonSerializer.Serialize(value); Console.WriteLine(); Console.WriteLine(json); deserialized = JsonSerializer.Deserialize <WikiLink>(json); Assert.AreEqual(value, deserialized); Assert.AreEqual(json, JsonSerializer.Serialize(deserialized)); value = new WikiLink(true, false, false, false, "Test Title", _Options.CategoryNamespace); json = JsonSerializer.Serialize(value); Console.WriteLine(); Console.WriteLine(json); deserialized = JsonSerializer.Deserialize <WikiLink>(json); Assert.AreEqual(value, deserialized); Assert.AreEqual(json, JsonSerializer.Serialize(deserialized)); value = new WikiLink(true, true, false, false, "Test Title", _Options.CategoryNamespace); json = JsonSerializer.Serialize(value); Console.WriteLine(); Console.WriteLine(json); deserialized = JsonSerializer.Deserialize <WikiLink>(json); Assert.AreEqual(value, deserialized); Assert.AreEqual(json, JsonSerializer.Serialize(deserialized)); value = new WikiLink(false, false, true, false, "Test Title", "Test Namespace"); json = JsonSerializer.Serialize(value); Console.WriteLine(); Console.WriteLine(json); deserialized = JsonSerializer.Deserialize <WikiLink>(json); Assert.AreEqual(value, deserialized); Assert.AreEqual(json, JsonSerializer.Serialize(deserialized)); }
/// <inheritdoc /> protected override async Task ProcessRecordAsync(CancellationToken cancellationToken) { using (var fs = new FileStream(File.FullName, FileMode.Open, FileAccess.Read, FileShare.Read, 1024 * 4, FileOptions.Asynchronous | FileOptions.SequentialScan)) { var titleLink = WikiLink.Parse(WikiSite, Title, BuiltInNamespaces.File); WikiUploadSource uploadSource; if (Chunked) { if (!ShouldProcess($"{File}", "Chunked stash")) { return; } var src = new ChunkedUploadSource(WikiSite, fs, File.Name) { DefaultChunkSize = 512 * 1024 }; var progress = new ProgressRecord(0, $"Stash {File}", null); WriteProgress(progress); do { var r = await src.StashNextChunkAsync(cancellationToken); if (r.ResultCode == UploadResultCode.Warning) { WriteWarning(r.Warnings.ToString()); } progress.PercentComplete = (int)(100.0 * src.UploadedSize / src.TotalSize); } while (!src.IsStashed); progress.RecordType = ProgressRecordType.Completed; uploadSource = src; } else { uploadSource = new StreamUploadSource(fs); } if (!ShouldProcess($"{File} -> {titleLink}", "Upload")) { return; } var result = await WikiSite.UploadAsync(Title, uploadSource, Comment, Force, Utility.ParseAutoWatchBehavior(Watch), cancellationToken); if (result.ResultCode == UploadResultCode.Warning) { WriteWarning(result.Warnings.ToString()); } WriteObject(result); } }
public async Task InterwikiLinkTests() { // We will not login onto any site… var homeSite = await Family.GetSiteAsync("test2"); var link = await WikiLink.ParseAsync(homeSite, Family, "WikiPedia:SANDBOX"); AssertWikiLink(link, null, "Wikipedia", "SANDBOX"); link = await WikiLink.ParseAsync(homeSite, Family, "FR___:_ __Wp__ _: SANDBOX"); AssertWikiLink(link, "fr", "Wikipédia", "SANDBOX"); link = await WikiLink.ParseAsync(homeSite, Family, "EN:fr: LZH:Project:SANDBOX"); AssertWikiLink(link, "lzh", "維基大典", "SANDBOX"); }
/// <summary> /// Initializes a new instance of <see cref="WikiPage"/> from page title. /// </summary> /// <param name="site">The wiki site this page is on.</param> /// <param name="title">Page title with or without namespace prefix.</param> /// <param name="defaultNamespaceId">The default namespace ID for page title without namespace prefix.</param> /// <remarks>The initialized instance does not contain any live information from MediaWiki site. /// Use <see cref="RefreshAsync()"/> to fetch for information from server.</remarks> public WikiPage(WikiSite site, string title, int defaultNamespaceId) { if (site == null) { throw new ArgumentNullException(nameof(site)); } if (string.IsNullOrWhiteSpace(title)) { throw new ArgumentNullException(nameof(title)); } Site = site; var parsedTitle = WikiLink.Parse(site, title, defaultNamespaceId); PageStub = new WikiPageStub(parsedTitle.FullTitle, parsedTitle.Namespace !.Id); }
public WikiPage(WikiSite site, string title, int defaultNamespaceId) { if (site == null) { throw new ArgumentNullException(nameof(site)); } if (string.IsNullOrWhiteSpace(title)) { throw new ArgumentNullException(nameof(title)); } Site = site; WikiClient = Site.WikiClient; Debug.Assert(WikiClient != null); var parsedTitle = WikiLink.Parse(site, title, defaultNamespaceId); Title = parsedTitle.FullTitle; NamespaceId = parsedTitle.Namespace.Id; }
public async Task <PageEditorViewModel> OpenPageEditorAsync(WikiSiteViewModel wikiSite, string pageTitle) { var site = await wikiSite.GetSiteAsync(); var normalizedTitle = WikiLink.NormalizeWikiLink(site, pageTitle); var editor = _ChildViewModelService.Documents .OfType <PageEditorViewModel>() .Where(vm => vm.SiteContext == wikiSite) .FirstOrDefault(vm => vm.WikiPage.Title == normalizedTitle); if (editor == null) { editor = CreatePageEditor(wikiSite); editor.SetWikiPageAsync(pageTitle).Forget(); _ChildViewModelService.Documents.Add(editor); } editor.IsActive = true; return(editor); }
/// <summary> /// Get all possible namespace-alias - title combinations. /// </summary> private IEnumerable <string> PageTitleCombinations(string title, Site site, bool leadingColon, bool transclusion) { var link = WikiLink.Parse(site, title); if (link.Namespace.Id == BuiltInNamespaces.Main) { if (transclusion) { return new[] { ":" + link.Title } } ; return(new[] { link.Title }); } if (transclusion && link.Namespace.Id == BuiltInNamespaces.Template) { return new[] { link.Title } } ; return(link.Namespace.Aliases.Concat(new[] { link.Namespace.CanonicalName, link.Namespace.CustomName }) .Distinct().Where(n => n != null) .Select(n => (leadingColon ? ":" : null) + n + ":" + link.Title)); }
/// <summary> /// Infers MediaWiki content model name from the title of the page. /// This is for MediaWiki version lower than 1.21 . /// </summary> public static string InferContentModelFromTitle(WikiLink title) { if (title == null) { throw new ArgumentNullException(nameof(title)); } if (title.Namespace.Id == BuiltInNamespaces.MediaWiki || title.Namespace.Id == BuiltInNamespaces.User) { if (title.Title.EndsWith(".js", StringComparison.OrdinalIgnoreCase)) { return(ContentModels.JavaScript); } if (title.Title.EndsWith(".css", StringComparison.OrdinalIgnoreCase)) { return(ContentModels.Css); } } if (title.Namespace.CanonicalName == "Module") { return(ContentModels.Scribunto); } return(ContentModels.Wikitext); }
/// <summary> /// Create an instance of <see cref="WikiPage"/> or its derived class, /// depending on the namespace the page is in. /// </summary> /// <param name="site">Site instance.</param> /// <param name="title">Title of the page, with or without namespace prefix.</param> /// <param name="defaultNamespaceId"> /// The namespace id of the page used when there's no explicit namespace prefix in <paramref name="title"/>. /// See <see cref="BuiltInNamespaces"/> for a list of possible values. /// </param> /// <exception cref="ArgumentNullException">Either <paramref name="site"/> or <paramref name="title"/> is <c>null</c>.</exception> /// <exception cref="ArgumentException"><paramref name="title"/> has invalid title patterns.</exception> /// <exception cref="InvalidOperationException"><paramref name="title"/> is an interwiki link.</exception> public static WikiPage FromTitle(WikiSite site, string title, int defaultNamespaceId) { if (site == null) { throw new ArgumentNullException(nameof(site)); } if (title == null) { throw new ArgumentNullException(nameof(title)); } WikiLink link; try { link = WikiLink.Parse(site, title, defaultNamespaceId); } catch (ArgumentException ex) { throw new ArgumentException(ex.Message, nameof(title), ex); } if (link.InterwikiPrefix != null) { throw new InvalidOperationException($"Interwiki title is not supported: {title} ."); } switch (link.Namespace.Id) { case BuiltInNamespaces.Category: return(new CategoryPage(site, title)); case BuiltInNamespaces.File: return(new CategoryPage(site, title)); default: return(new WikiPage(site, title, defaultNamespaceId)); } }
/// <summary> /// Initializes the generator from the site and category title. /// </summary> /// <param name="site">Site instance.</param> /// <param name="categoryTitle">Title of the category, with or without Category: prefix.</param> public CategoryMembersGenerator(WikiSite site, string categoryTitle) : base(site) { CategoryTitle = WikiLink.NormalizeWikiLink(site, categoryTitle, BuiltInNamespaces.Category); }
public async Task TestMethod4() { var site = await WpTest2SiteAsync; Assert.Throws <ArgumentException>(() => WikiLink.Parse(site, "Project:")); }
public static async Task <Post> PostWallMessageAsync(WikiaSite site, object scopeInst, WikiPageStub owner, string messageTitle, string messageBody, IEnumerable <string> relatedPages, CancellationToken cancellationToken) { Debug.Assert(site != null); Debug.Assert(owner.HasTitle); using (site.BeginActionScope(scopeInst, owner)) { var tokenPurged = false; var pageTitle = owner.Title; var pageNamespaceId = owner.NamespaceId; if (pageTitle.StartsWith("Message Wall:", StringComparison.OrdinalIgnoreCase)) { pageTitle = pageTitle.Substring(13); if (!owner.HasNamespaceId) { pageNamespaceId = WikiaNamespaces.MessageWall; } } else if (pageTitle.StartsWith("Board:", StringComparison.OrdinalIgnoreCase)) { pageTitle = pageTitle.Substring(6); if (!owner.HasNamespaceId) { pageNamespaceId = WikiaNamespaces.Thread; } } else { var link = WikiLink.Parse(site, owner.Title); pageTitle = link.Title; pageNamespaceId = link.Namespace.Id; } var queryParams = new OrderedKeyValuePairs <string, object> { { "token", null }, { "controller", "WallExternal" }, { "method", "postNewMessage" }, { "format", "json" }, { "pagenamespace", pageNamespaceId }, { "pagetitle", pageTitle }, { "messagetitle", messageTitle }, { "body", messageBody }, { "notifyeveryone", 0 }, { "convertToFormat", "" }, }; if (relatedPages != null) { foreach (var title in relatedPages) { queryParams.Add("relatedTopics[]", title); } } BEGIN: queryParams["token"] = await site.GetTokenAsync("edit", cancellationToken); var jresult = await site.InvokeNirvanaAsync(new WikiaQueryRequestMessage(queryParams, true), WikiaJsonResonseParser.Default, cancellationToken); if (!string.Equals((string)jresult["status"], "True", StringComparison.OrdinalIgnoreCase)) { var errorMessage = (string)jresult["errormsg"]; if (errorMessage != null) { if (!tokenPurged) { if (errorMessage.IndexOf("There seems to be a problem with your login session", StringComparison.OrdinalIgnoreCase) >= 0) { await site.GetTokenAsync("edit", true, cancellationToken); tokenPurged = true; goto BEGIN; } } } errorMessage = "Status code indicates a failure: " + (string)jresult["status"]; throw new OperationFailedException(errorMessage); } var text = (string)jresult["message"]; var doc = new HtmlDocument(); doc.LoadHtml(text); var node = doc.DocumentNode.SelectSingleNode("li"); if (node == null) { throw new UnexpectedDataException("Cannot locate the comment text node in the Wikia API response."); } return(Post.FromHtmlNode(site, owner, node)); } }
//content can be // Stream file content // string url to fetch // UploadResult the previous failed upload private static async Task <UploadResult> UploadAsyncInternal(WikiSite site, object content, string title, string comment, bool ignoreWarnings, AutoWatchBehavior watch, CancellationToken cancellationToken) { if (site == null) { throw new ArgumentNullException(nameof(site)); } if (content == null) { throw new ArgumentNullException(nameof(content)); } if (title == null) { throw new ArgumentNullException(nameof(title)); } var link = WikiLink.Parse(site, title); if (link.Namespace.Id != BuiltInNamespaces.File) { throw new ArgumentException($"Invalid namespace for file title: {title} .", nameof(title)); } var token = await site.GetTokenAsync("edit"); long?streamPosition = null; HttpContent RequestFactory() { var requestContent = new MultipartFormDataContent { { new StringContent("json"), "format" }, { new StringContent("upload"), "action" }, { new StringContent(Utility.ToWikiQueryValue(watch)), "watchlist" }, { new StringContent(token), "token" }, { new StringContent(link.Title), "filename" }, { new StringContent(comment), "comment" }, }; if (content is Stream streamContent) { if (streamPosition < 0) { return(null); } // Memorize/reset the stream position. if (streamContent.CanSeek) { if (streamPosition == null) { streamPosition = streamContent.Position; } else { streamContent.Position = streamPosition.Value; } Debug.Assert(streamPosition >= 0); } else { // Mark for do-not-retry. streamPosition = -1; } requestContent.Add(new KeepAlivingStreamContent(streamContent), "file", title); } else if (content is string stringContent) { requestContent.Add(new StringContent(stringContent), "url"); } else if (content is UploadResult resultContent) { var key = (resultContent).FileKey; if (string.IsNullOrEmpty(key)) { throw new InvalidOperationException("The specified UploadResult has no valid FileKey."); } // sessionkey: Same as filekey, maintained for backward compatibility (deprecated in 1.18) requestContent.Add(new StringContent(key), site.SiteInfo.Version >= new Version(1, 18) ? "filekey" : "sessionkey"); } else { Debug.Assert(false, "Unrecognized content argument type."); } if (ignoreWarnings) { requestContent.Add(new StringContent(""), "ignorewarnings"); } return(requestContent); } site.Logger?.Info(site, $"Uploading: {link.Title} ."); var jresult = await site.PostContentAsync(RequestFactory, cancellationToken); var result = jresult["upload"].ToObject <UploadResult>(Utility.WikiJsonSerializer); site.Logger?.Info(site, $"Upload[{link.Title}]: {result}."); switch (result.ResultCode) { case UploadResultCode.Warning: throw new UploadException(result); default: // UploadResult.Result setter should have thrown an exception. Debug.Assert(result.ResultCode == UploadResultCode.Success || result.ResultCode == UploadResultCode.Continue); break; } return(result); }
/// <summary> /// Given the summary of a page asynchronously. /// </summary> /// <exception cref="ArgumentNullException">title is null.</exception> /// <exception cref="ArgumentException">title is invalid.</exception> /// <remarks>Do not modify the returned objects.</remarks> public async Task <IList <PageInfo> > GetPageSummaryAsync(IEnumerable <string> titles) { if (titles == null) { throw new ArgumentNullException(nameof(titles)); } var site = await GetSiteAsync(); var titleList = titles.Select(t => WikiLink.NormalizeWikiLink(site, t)).ToArray(); var pagesToCheck = new List <Page>(); var pagesToFetch = new List <Page>(); var pagesToCheckRedirect = new List <Tuple <Page, PageInfo> >(); var parser = new Lazy <WikitextParser>(); // First, check whether we need to contact the server for each of the page title. foreach (var title in titleList) { var cached = _PageSummaryCache.TryGetValue(title); if (cached != null && DateTime.Now - cached.Item2 < _CacheExpiry) { continue; } pagesToCheck.Add(new Page(site, title)); } await pagesToCheck.RefreshAsync(); foreach (var page in pagesToCheck) { var cached = _PageSummaryCache.TryGetValue(page.Title); // Check if the cache is obsolete. if (cached != null && page.LastRevisionId == cached.Item1.LastRevisionId) { _PageSummaryCache[page.Title] = Tuple.Create(cached.Item1, DateTime.Now); continue; } // We need to fetch the whole page. pagesToFetch.Add(page); } // Fetch information & content. await pagesToFetch.RefreshAsync(PageQueryOptions.FetchContent); foreach (var page in pagesToFetch) { var info = PageInfoBuilder.BuildBasicInfo(page, parser.Value); _PageSummaryCache[page.Title] = Tuple.Create(info, DateTime.Now); if (page.IsRedirect) { pagesToCheckRedirect.Add(Tuple.Create(page, info)); } } // Fetch redirects. await pagesToCheckRedirect.Select(t => t.Item1).RefreshAsync(PageQueryOptions.ResolveRedirects); foreach (var tp in pagesToCheckRedirect) { var path = tp.Item1.RedirectPath.ToList(); // Add & complete redirect path path.Add(tp.Item1.Title); tp.Item2.RedirectPath = path; } return(titleList.Select(t => _PageSummaryCache[t].Item1).ToArray()); }
private void AssertWikiLink(WikiLink link, string interwiki, string ns, string localTitle) { Assert.Equal(interwiki, link.InterwikiPrefix); Assert.Equal(ns, link.NamespaceName); Assert.Equal(localTitle, link.Title); }