Пример #1
0
        void DownloadAndProcess()
        {
            Invoke((MethodInvoker) delegate()
            {
                // enable those controls which are initially disabled
                btnTransfer.Enabled = txtNormName.Enabled = chkDeleteAfter.Enabled =
                    chkIgnoreWarnings.Enabled = toolBarLinks.Enabled = coolCat.Enabled = true;

                txtLocalText.Text = txtCommonsText.Text = lblName.Text = lblRevision.Text = lblDimensions.Text = "";
                coolCat.ClearCategories();
                pictureBox1.Image = null;
                pictureBox1.Cursor = Cursors.Default;
                lblPastRevisions.Visible = btnPastRevisions.Visible = lblViewExif.Visible =
                    btnViewExif.Visible = false;
                lstFileLinks.ForeColor = SystemColors.GrayText;
                lstFileLinks.Items.Clear();
                chkIgnoreWarnings.Checked = false;
                ClearWarnings();
                lnkCommonsFile.Enabled = lnkLocalFile.Enabled = lnkGoogleImageSearch.Enabled =
                    lnkGoToFileLink.Enabled = false;
                SetTransferButtonDownloading(true);

                textBox1.Text = textBox1.Text.Trim();
            });
            if (ImageDataDownloader != null)
                ImageDataDownloader.CancelAsync();

            CurrentFileName = Regex.Replace(textBox1.Text, @"^\w+:", "", RegexOptions.IgnoreCase);

            EnableForm(false);

            ImageInfos = null;
            ImageDatas = null;
            string text = "";
            MorebitsDotNet.ActionCompleted sentry = new MorebitsDotNet.ActionCompleted(2);
            sentry.Done += new MorebitsDotNet.ActionCompleted.Action(delegate()
            {
                if (ImageInfos == null)
                    return;  // too much on at once

                // identify potential problems
                string textLowercase = text.ToLower();
                List<string> potentialProblems = new List<string>();
                if (Regex.IsMatch(text, "{{((db-)?now ?commons|" + Regex.Escape(LocalWikiData.NowCommonsTag) + ")", RegexOptions.IgnoreCase))
                {
                    potentialProblems.Add("• " + Localization.GetString("NowCommonsPotentialProblem"));
                    if (CurrentFileSource == FileSources.Category)
                    {
                        RandomBlacklist.Add(RandomCurrentIndex);  // don't turn up this file again
                        RandomImage(null, null);
                        return;
                    }
                }
                foreach (LocalWikiData.PotentialProblem problem in LocalWikiData.PotentialProblems)
                {
                    try
                    {
                        if (problem.IsRegex ?
                            Regex.IsMatch(text, problem.Test, RegexOptions.IgnoreCase) :
                            textLowercase.Contains(problem.Test.ToLower()))
                        {
                            potentialProblems.Add("• " + problem.Message);
                        }
                    }
                    catch (ArgumentException e)
                    {
                        ErrorHandler(Localization.GetString("LocalWikiDataError") + "\n\n" +
                            Localization.GetString("LocalWikiDataRegexError", "PotentialProblem IfRegex") + "\n\n" + e.Message);
                    }
                }

                if (potentialProblems.Count > 0)
                {
                    Invoke((MethodInvoker) delegate()
                    {
                        foreach (string i in potentialProblems)
                            AddWarning(i, WarningBoxType.Warning);
                    });
                }

                // start building the new file description page
                string origUploader = ImageInfos[ImageInfos.Count - 1].Attributes["user"].Value;

                // clean up local templates, etc.
                string prefix = GetCurrentInterwikiPrefix(false);
                try
                {
                    text = Regex.Replace(text, LocalWikiData.CopyToCommonsRegex, "", RegexOptions.IgnoreCase | RegexOptions.Compiled);
                }
                catch (ArgumentException e)
                {
                    ErrorHandler(Localization.GetString("LocalWikiDataError") + "\n\n" +
                        Localization.GetString("LocalWikiDataRegexError", "CopyToCommonsRegex") + "\n\n" + e.Message);
                }
                //text = Regex.Replace(text, "== ?(Summary|Licensing:?) ?== *\n", "\n");
                text = Regex.Replace(text, "== ?((" + LocalWikiData.Summary + ")|{{int:filedesc}}) ?== *\n", "", RegexOptions.IgnoreCase);
                text = Regex.Replace(text, "\n?\n?== ?((" + LocalWikiData.Licensing + ")|{{int:license}}) ?== *\n", "\n\n== {{int:license-header}} ==\n", RegexOptions.IgnoreCase);
                text = Regex.Replace(text, @"\[\[:?", "[[" + prefix + ":", RegexOptions.Compiled);
                text = Regex.Replace(text, @"\[\[" + prefix + @":([^\|\]]+)\]\]", new MatchEvaluator(delegate(Match m)
                    {
                        string linktext = m.Groups[1].Value;
                        string linktextLower = linktext.ToLower();
                        if (!(linktextLower.StartsWith("category:")) && !(linktextLower.StartsWith(LocalWikiData.CategoryNamespace.ToLower())))
                            return "[[" + prefix + ":" + linktext + "|" + linktext + "]]";
                        return "<!-- [[" + linktext + "]] -->";  // comment out categories
                    }), RegexOptions.Compiled);
                // this next one is redundant to the above BUT still needed for categories with sortkeys (?!)
                text = Regex.Replace(text, @"\[\[" + prefix + @":(Category:[^\]]+\]\])", "<!-- [[$1 -->", RegexOptions.Compiled);

                // per-wiki cleanup
                foreach (KeyValuePair<string, string> replacement in LocalWikiData.Replacements)
                {
                    try
                    {
                        text = Regex.Replace(text, replacement.Key, replacement.Value.Replace("\\n", "\n"),
                            RegexOptions.IgnoreCase);
                    }
                    catch (ArgumentException e)
                    {
                        ErrorHandler(Localization.GetString("LocalWikiDataError") + "\n\n" +
                            Localization.GetString("LocalWikiDataRegexError", "Replacement") + "\n\n" + e.Message);
                    }
                }

                // amend self-license tags
                string beforeSelfTagCheck = text;
                foreach (KeyValuePair<string, string> replacement in LocalWikiData.SelfLicenseReplacements)
                {
                    try
                    {
                        text = Regex.Replace(text, replacement.Key, replacement.Value.Replace("\\n", "\n")
                            .Replace("%%OriginalUploader%%", origUploader).Replace("%%InterwikiLinkPrefix%%", prefix),
                            RegexOptions.IgnoreCase);
                    }
                    catch (ArgumentException e)
                    {
                        ErrorHandler(Localization.GetString("LocalWikiDataError") + "\n\n" +
                            Localization.GetString("LocalWikiDataRegexError", "SelfLicenseReplacement") + "\n\n" + e.Message);
                    }
                }
                bool selfLicense = (text != beforeSelfTagCheck);

                text = text.Trim();

                // the character index at which the information tag finishes (doesn't have to be exact)
                int infoTagEnd = 0;

                string languageCode = GetCurrentLanguageCode();

                if (!textLowercase.Contains("{{information") &&
                    !Regex.IsMatch(text, "{{" + LocalWikiData.Information, RegexOptions.IgnoreCase))
                {
                    string detectedDesc = Regex.Replace(text, "{{[^}]*}}", "", RegexOptions.IgnoreCase);
                    detectedDesc = Regex.Replace(detectedDesc, "==[^=]*==", "", RegexOptions.IgnoreCase);
                    detectedDesc = detectedDesc.Split('\n')[0];
                    if (detectedDesc.Length > 0)
                        text = text.Replace(detectedDesc, "");

                    XmlNode exifDateNode = ImageInfos[ImageInfos.Count - 1].SelectSingleNode("metadata/metadata[@name=\"DateTime\"]");
                    string exifDate = null;
                    if (exifDateNode != null)
                    {
                        exifDate = exifDateNode.Attributes["value"].Value;
                        if (Regex.IsMatch(exifDate, @"^\d\d\d\d:\d\d:\d\d") && !exifDate.StartsWith("0000"))
                            exifDate = exifDate.Substring(0, 10).Replace(':', '-');
                        else
                            exifDate = null;
                    }

                    // Note: pipe replacement was commented out because it caused problems with piped wikilinks in the
                    // detected description. Of course, now any literal pipes will cause problems...
                    var infoTag =
            "== {{int:filedesc}} ==\n" +
            "{{Information\n" +
            "|Description    = " + (languageCode.Length > 0 ? ("{{" + languageCode + "|1=" + detectedDesc.Trim()/*.Replace("|", "&#124;")*/ + "}}") : detectedDesc.Trim().Replace("|", "&#124;")) + "\n" +
            "|Date           = " + (exifDate != null ? "{{according to EXIF data|" + exifDate + "}}\n" : "{{original upload date|" + FormatIsoDate(ImageInfos[ImageInfos.Count - 1]) + "}}\n") +
            "|Source         = {{own work by original uploader}} <!-- " + Localization.GetString("ChangeIfNotOwnWork") + " -->\n" +
            "|Author         = " + (selfLicense ? ("[[" + prefix + ":User:"******"user"].Value + "|]]\n") : "\n") +
            "|Permission     = \n" +
            "|Other_versions = \n" +
            "}}\n\n";
                    text = infoTag + text;

                    infoTagEnd = infoTag.Length - 8;  // -8 for sanity
                }
                else
                {
                    text = "== {{int:filedesc}} ==\n" + text;

                    string errorTopicText = "";
                    try
                    {
                        errorTopicText = "Information";
                        text = Regex.Replace(text, @"{{\s*(" + LocalWikiData.Information + @")", "{{Information", RegexOptions.IgnoreCase);

                        string paramStart = @"({{Information({{[^{}]*}}|[^{}])*)\|\s*(";
                        string paramEnd = @")\s*= *";
                        errorTopicText = "Description";
                        text = Regex.Replace(text, paramStart + LocalWikiData.Description + paramEnd, "$1|Description    = ", RegexOptions.IgnoreCase);
                        errorTopicText = "Date";
                        text = Regex.Replace(text, paramStart + LocalWikiData.Date + paramEnd, "$1|Date           = ", RegexOptions.IgnoreCase);
                        errorTopicText = "Source";
                        text = Regex.Replace(text, paramStart + LocalWikiData.Source + paramEnd, "$1|Source         = ", RegexOptions.IgnoreCase);
                        errorTopicText = "Author";
                        text = Regex.Replace(text, paramStart + LocalWikiData.Author + paramEnd, "$1|Author         = ", RegexOptions.IgnoreCase);
                        errorTopicText = "Permission";
                        text = Regex.Replace(text, paramStart + LocalWikiData.Permission + paramEnd, "$1|Permission     = ", RegexOptions.IgnoreCase);
                        errorTopicText = "Other_versions";
                        text = Regex.Replace(text, paramStart + LocalWikiData.Other_versions + paramEnd, "$1|Other_versions = ", RegexOptions.IgnoreCase);
                    }
                    catch (ArgumentException e)
                    {
                        ErrorHandler(Localization.GetString("LocalWikiDataError") + "\n\n" +
                            Localization.GetString("LocalWikiDataRegexError", errorTopicText) + "\n\n" + e.Message);
                    }

                    if (languageCode.Length > 0 && !text.Contains("{{" + languageCode + "|"))
                        text = Regex.Replace(text, @"({{Information[\r\n]* *\| ?Description *= *)([^\r\n ][^\r\n]+)([\r\n])",
                            "$1{{" + languageCode + "|1=$2}}$3", RegexOptions.IgnoreCase);

                    Match infoTagMatch = Regex.Match(text, @"{{\s*information\s*(\|({{[^{}]*}}|[^{}])*)?}}", RegexOptions.IgnoreCase);
                    infoTagEnd = infoTagMatch.Index + infoTagMatch.Length - 8;  // -8 for sanity
                }

                // assume first template is a license tag
                if (!text.Contains("{{int:license-header}}"))
                {
                    bool hadAnySuccessYet = false;
                    text = Regex.Replace(text, "\n?\n?{{", delegate(Match m)
                    {
                        if (m.Index < infoTagEnd || hadAnySuccessYet)
                            return m.Groups[0].Value;
                        hadAnySuccessYet = true;
                        return "\n\n== {{int:license-header}} ==\n{{";
                    });
                }

                text += "\n\n== {{Original upload log}} ==";
                if (Settings.CommonsDomain == Settings.DefaultCommonsDomain)
                {
                    text += "\n\n{{transferred from|" + Settings.LocalDomain + "||ftcg}} {{original description page|" +
                        Settings.LocalDomain + "|" + Uri.EscapeDataString(CurrentFileName.Replace(' ', '_')) + "}}";
                }

                text += "\n\n{| class=\"wikitable\"\n! {{int:filehist-datetime}} !! {{int:filehist-dimensions}} !! {{int:filehist-user}} !! {{int:filehist-comment}}";
                foreach (XmlNode n in ImageInfos)
                {
                    text += "\n|-\n| " + FormatTimestamp(n) + " || " + FormatDimensions(n) + " || ";

                    // check if the username has been RevDel'd (for admins, the user attribute
                    // will be present, and we don't want to copy the hidden username to Commons,
                    // so we need to check the userhidden attribute)
                    if (n.Attributes["userhidden"] != null || n.Attributes["user"] == null)
                        text += "<span class=\"history-deleted\">{{int:rev-deleted-user}}</span>";
                    else
                        text += "{{uv|" + n.Attributes["user"].Value + "|" + GetCurrentInterwikiPrefix(true) + ":}}";

                    text += " || ";

                    // same deal for comment/commenthidden
                    if (n.Attributes["commenthidden"] != null || n.Attributes["comment"] == null)
                        text += "<span class=\"history-deleted\">{{int:rev-deleted-comment}}</span>";
                    else
                        text += "<nowiki>" + n.Attributes["comment"].Value + "</nowiki>";
                }
                text += "\n|}";

                // remove multiple line breaks
                text = Regex.Replace(text, @"[\r\n]{3,}", "\n\n");

                Invoke((MethodInvoker) delegate()
                    {
                        txtCommonsText.Text = text.Replace("\n", "\r\n");
                        lnkLocalFile.Enabled = lnkGoogleImageSearch.Enabled = true;
                        lnkCommonsFile.Enabled = false;
                        lblFileLinks.Visible = lstFileLinks.Visible = true;
                        if (lstFileLinks.ForeColor == SystemColors.GrayText)
                        {
                            lstFileLinks.Items.Clear();
                            lstFileLinks.Items.Add(Localization.GetString("Loading"));
                        }
                        EnableForm(true);
                    });
            });
            sentry.Finally += new MorebitsDotNet.ActionCompleted.Action(delegate()
            {
                Invoke((MethodInvoker) delegate()
                {
                    EnableForm(true);
                });
            });

            // download image file
            StringDictionary query = new StringDictionary
            {
                { "action", "query" },
                { "prop", "imageinfo|info" },
                { "iiprop", "comment|timestamp|user|url|size|mime|metadata" },
                { "iilimit", "500" },
                { "iiurlwidth", pictureBox1.Width.ToString() },
                { "iiurlheight", pictureBox1.Height.ToString() },
                { "titles", "File:" + CurrentFileName + "|File talk:" + CurrentFileName },
                { "redirects", "true" }
            };
            MorebitsDotNet.PostApi(Wiki.Local, query, delegate(XmlDocument doc)
            {
                XmlNode filePage = doc.SelectSingleNode("//page[@ns=6]");  // was doc.GetElementsByTagName("page")[0]

                switch (filePage.Attributes["imagerepository"].Value)
                {
                    case "shared":
                        ErrorHandler(Localization.GetString("AlreadyCommons"));
                        sentry.Fail();
                        return;
                    case "":
                        if (filePage.Attributes["missing"] != null)
                            ErrorHandler(Localization.GetString("ImageMissing"));
                        else
                            ErrorHandler(Localization.GetString("NoFile"));
                        sentry.Fail();
                        return;
                }

                ImageInfos = doc.GetElementsByTagName("ii");
                ImageDatas = new byte[ImageInfos.Count][];

                Invoke((MethodInvoker) delegate()
                {
                    lblName.Text = filePage.Attributes["title"].Value;

                    if (ImageInfos.Count > 1)
                    {
                        lblPastRevisions.Visible = btnPastRevisions.Visible = true;
                        lblPastRevisions.Text = ((ImageInfos.Count == 2) ?
                            Localization.GetString("OneEarlierVersion_Label") :
                            Localization.GetString("EarlierVersions_Format", (ImageInfos.Count - 1).ToString()));
                    }
                    else
                        lblPastRevisions.Visible = btnPastRevisions.Visible = false;
                });

                // notify about presence of file talk page (if it is over 120 bytes in size)
                XmlNode fileTalkPage = doc.SelectSingleNode("//page[@ns=7]");
                if (fileTalkPage.Attributes["missing"] == null)
                {
                    string warningtext = "• " + Localization.GetString("ContentOnTalkPage");
                    if (fileTalkPage.Attributes["length"] != null &&
                        int.Parse(fileTalkPage.Attributes["length"].Value) > int.Parse(LocalWikiData.FileTalkMinimumSize))
                    {
                        warningtext = warningtext.Replace("{1}", " (" + int.Parse(fileTalkPage.Attributes["length"].Value).
                            ToString("n0", CultureInfo.InvariantCulture) + " bytes)");
                        AddWarningLink(warningtext, Localization.GetString("TalkPage"), delegate(object sender, LinkLabelLinkClickedEventArgs e)
                        {
                            try
                            {
                                Process.Start(MorebitsDotNet.GetProtocol() + "://" + Settings.LocalDomain + ".org/wiki/File_talk:" +
                                    CurrentFileName);
                            }
                            catch (Exception)
                            {
                                ErrorHandler(Localization.GetString("LinkVisitFailed"));
                            }
                        }, WarningBoxType.Warning);
                    }
                }

                // download the file and display a thumbnail (also display metadata)
                SelectedRevisions = new int[] { 0 };
                DownloadFileAndDisplayThumb();

                sentry.DoneOne();
            }, ErrorHandler, WebRequestMethods.Http.Get);

            // get wikitext of file description page
            query = new StringDictionary
            {
                { "action", "query" },
                { "prop", "revisions" },
                { "rvprop", "content" },
                { "titles", "File:" + CurrentFileName },
                { "redirects", "true" }
            };
            MorebitsDotNet.PostApi(Wiki.Local, query, delegate(XmlDocument doc)
            {
                if (doc.GetElementsByTagName("page")[0].Attributes["missing"] != null)
                {
                    // MessageBox.Show("Image does not exist on enwiki");    don't need to tell user twice
                    sentry.Fail();
                    return;
                }

                text = doc.GetElementsByTagName("rev")[0].InnerText;

                Invoke((MethodInvoker) delegate()
                {
                    txtLocalText.Text = text.Replace("\n", "\r\n");
                });

                XmlNodeList ns = doc.GetElementsByTagName("n");
                if (ns.Count > 0)
                    Invoke((MethodInvoker) delegate()
                    {
                        CurrentFileName = ns[0].Attributes["to"].Value;
                        CurrentFileName = CurrentFileName.Substring(CurrentFileName.IndexOf(':') + 1);
                    });
                Invoke((MethodInvoker) delegate()
                {
                    txtNormName.Text = "File:" + CurrentFileName;
                });

                sentry.DoneOne();
            }, ErrorHandler, WebRequestMethods.Http.Get);

            // get file links (not in sentry)
            query = new StringDictionary
            {
                { "action", "query" },
                { "list", "imageusage" },
                { "iulimit", "20" },
                { "iutitle", "File:" + CurrentFileName },
                { "rawcontinue", "" },
            };
            // prevent race conditions
            object current = new object();
            Invoke((MethodInvoker) delegate() { lstFileLinks.Tag = current; });
            MorebitsDotNet.PostApi(Wiki.Local, query, delegate(XmlDocument doc)
            {
                if (lstFileLinks.Tag != current)
                    return;
                Invoke((MethodInvoker) delegate()
                {
                    lstFileLinks.Items.Clear();
                    XmlNodeList ius = doc.GetElementsByTagName("iu");
                    if (ius.Count == 0)
                    {
                        lstFileLinks.Items.Add(Localization.GetString("NoImageUsages_Label"));
                        return;
                    }

                    lstFileLinks.ForeColor = SystemColors.ControlText;
                    foreach (XmlNode i in ius)
                        lstFileLinks.Items.Add(i.Attributes["title"].Value);
                    if (doc.GetElementsByTagName("query-continue").Count > 0)
                        lstFileLinks.Items.Add("<<" + Localization.GetString("SeeWikiForFullList_Label") + ">>");
                });
            }, ErrorHandler, WebRequestMethods.Http.Get);
        }
Пример #2
0
        void DownloadAndProcess()
        {
            Invoke(new Action(delegate()
            {
                textBox2.Text = textBox3.Text = lblName.Text = lblRevision.Text = lblDimensions.Text = "";
                pictureBox1.Image = null;
                pictureBox1.Cursor = Cursors.Default;
                lblPastRevisions.Visible = btnPastRevisions.Visible = lblViewExif.Visible =
                    btnViewExif.Visible = false;
                lstFileLinks.ForeColor = SystemColors.GrayText;
                lstFileLinks.Items.Clear();
                chkIgnoreWarnings.Checked = false;
                HideWarningBox();
                lnkCommonsFile.Enabled = lnkLocalFile.Enabled = lnkGoogleImageSearch.Enabled =
                    lnkGoToFileLink.Enabled = false;

                textBox1.Text = textBox1.Text.Trim();
            }));
            if (cl != null)
                cl.CancelAsync();

            filename = "File:" + Regex.Replace(textBox1.Text, @"^\w+:", "", RegexOptions.IgnoreCase);

            EnableForm(false);

            iis = null;
            ImageData = null;
            string text = "";
            MorebitsDotNet.ActionCompleted sentry = new MorebitsDotNet.ActionCompleted(2);
            sentry.Done += new Action(delegate()
            {
                if (iis == null)
                    return;  // too much on at once

                // identify potential problems
                string textLowercase = text.ToLower();
                List<string> potentialProblems = new List<string>();
                if (Regex.IsMatch(text, "{{((db-)?now ?commons|" + Regex.Escape(LocalWikiData.NowCommonsTag) + ")", RegexOptions.IgnoreCase))
                {
                    potentialProblems.Add("• " + Localization.GetString("NowCommonsPotentialProblem"));
                    if (CurrentFileSource == FileSources.Category)
                    {
                        RandomBlacklist.Add(RandomCurrentIndex);  // don't turn up this file again
                        RandomImage(null, null);
                        return;
                    }
                }
                foreach (LocalWikiData.PotentialProblem problem in LocalWikiData.PotentialProblems)
                {
                    if (problem.IsRegex ?
                        Regex.IsMatch(text, problem.Test, RegexOptions.IgnoreCase) :
                        textLowercase.Contains(problem.Test.ToLower()))
                    {
                        potentialProblems.Add("• " + problem.Message);
                    }
                }

                if (potentialProblems.Count > 0)
                {
                    Invoke(new Action(delegate()
                    {
                        ShowWarningBox(false, String.Join("\n", potentialProblems.ToArray()));
                    }));
                }

                // start building the new file description page
                string origUploader = iis[iis.Count - 1].Attributes["user"].Value;

                // clean up local templates, etc.
                string prefix = GetCurrentInterwikiPrefix(false);
                text = Regex.Replace(text, LocalWikiData.CopyToCommonsRegex, "", RegexOptions.IgnoreCase | RegexOptions.Compiled);
                //text = Regex.Replace(text, "== ?(Summary|Licensing:?) ?== *\n", "\n");
                text = Regex.Replace(text, "== ?(" + LocalWikiData.Summary + ") ?== *\n", "", RegexOptions.IgnoreCase);//"== {{int:filedesc}} ==\n");
                text = Regex.Replace(text, "\n?\n?== ?(" + LocalWikiData.Licensing + ") ?== *\n", "\n\n== {{int:license-header}} ==\n", RegexOptions.IgnoreCase);
                text = Regex.Replace(text, @"\[\[:?", "[[" + prefix + ":", RegexOptions.Compiled);
                text = Regex.Replace(text, @"\[\[" + prefix + @":([^\|\]]+)\]\]", new MatchEvaluator(delegate(Match m)
                    {
                        string linktext = m.Groups[1].Value;
                        string linktextLower = linktext.ToLower();
                        if (!(linktextLower.StartsWith("category:")) && !(linktextLower.StartsWith(LocalWikiData.CategoryNamespace.ToLower())))
                            return "[[" + prefix + ":" + linktext + "|" + linktext + "]]";
                        return "<!-- [[" + linktext + "]] -->";  // comment out categories
                    }), RegexOptions.Compiled);
                // this next one is redundant to the above BUT still needed for categories with sortkeys (?!)
                text = Regex.Replace(text, @"\[\[" + prefix + @":(Category:[^\]]+\]\])", "<!-- [[$1 -->", RegexOptions.Compiled);

                // per-wiki cleanup
                foreach (string replacement in LocalWikiData.Replacements.Keys)
                {
                    text = Regex.Replace(text, replacement, LocalWikiData.Replacements[replacement],
                        RegexOptions.IgnoreCase);
                }
                //text = Regex.Replace(text, "{{orphan image.*}}\n?", "", RegexOptions.IgnoreCase);
                //text = text.Replace("{{needs commons category}}", "");  // seems to always be lowercase with no params

                // amend self-license tags
                string beforeSelfTagCheck = text;
                foreach (string replacement in LocalWikiData.SelfLicenseReplacements.Keys)
                {
                    text = Regex.Replace(text, replacement,
                        LocalWikiData.SelfLicenseReplacements[replacement]
                            .Replace("%%OriginalUploader%%", origUploader)
                            .Replace("%%InterwikiLinkPrefix%%", prefix),
                        RegexOptions.IgnoreCase);
                }
                //text = Regex.Replace(text, "{{PD-self.*}}", "{{PD-user|" + origUploader + "|en}}", RegexOptions.IgnoreCase);
                //text = Regex.Replace(text, @"{{GFDL-self-with-disclaimers([^\}]*)}}", "{{GFDL-user-en-with-disclaimers|" + origUploader + "$1}}", RegexOptions.IgnoreCase);
                //text = Regex.Replace(text, @"{{GFDL-self([^\}]*)}}", "{{GFDL-user|" + origUploader + "|en$1}}", RegexOptions.IgnoreCase);
                //text = Regex.Replace(text, @"{{self([^\}]*)}}", "{{self$1|author=[[" + prefix + ":" + origUploader + "|]]}}", RegexOptions.IgnoreCase);
                bool selfLicense = (text != beforeSelfTagCheck);

                text = text.Trim();

                if (!textLowercase.Contains("{{information") &&
                    !Regex.IsMatch(text, "{{" + LocalWikiData.Information, RegexOptions.IgnoreCase))
                {
                    string detectedDesc = Regex.Replace(text, "{{[^}]*}}", "", RegexOptions.IgnoreCase);
                    detectedDesc = Regex.Replace(detectedDesc, "==[^=]*==", "", RegexOptions.IgnoreCase);
                    detectedDesc = detectedDesc.Split('\n')[0];
                    text = Regex.Replace(text, Regex.Escape(detectedDesc), "", RegexOptions.IgnoreCase);

                    XmlNode exifDateNode = iis[iis.Count - 1].SelectSingleNode("metadata/metadata[@name=\"DateTime\"]");
                    string exifDate = null;
                    if (exifDateNode != null)
                    {
                        exifDate = exifDateNode.Attributes["value"].Value;
                        if (Regex.IsMatch(exifDate, @"^\d\d\d\d:\d\d:\d\d"))
                            exifDate = exifDate.Substring(0, 10).Replace(':', '-');
                        else
                            exifDate = null;
                    }

                    string languageCode = GetCurrentLanguageCode();
                    var infoTag =
            "== {{int:filedesc}} ==\n" +
            "{{Information\n" +
            "|Description    = " + (languageCode.Length > 0 ? ("{{" + languageCode + "|1=" + detectedDesc.Trim().Replace("|", "&#124;") + "}}") : detectedDesc.Trim().Replace("|", "&#124;")) + "\n" +
            "|Date           = " + (exifDate != null ? "{{according to EXIF data|" + exifDate + "}}\n" : "{{original upload date|" + FormatIsoDate(iis[iis.Count - 1]) + "}}\n") +
            "|Source         = {{own}} <!-- " + Localization.GetString("ChangeIfNotOwnWork") + " -->\n" +
            "|Author         = " + (selfLicense ? ("[[" + prefix + ":User:"******"user"].Value + "|]]\n") : "\n") +
            "|Permission     = \n" +
            "|Other_versions = \n" +
            "}}\n\n";
                    text = infoTag + text;

                    // assume first template is a license tag
                    if (!text.Contains("{{int:license-header}}"))
                    {
                        bool hadAnySuccessYet = false;
                        text = Regex.Replace(text, "\n?\n?{{", delegate(Match m)
                        {
                            if (m.Index < infoTag.Length || hadAnySuccessYet)
                                return m.Groups[0].Value;
                            hadAnySuccessYet = true;
                            return "\n\n== {{int:license-header}} ==\n{{";
                        });
                    }
                }
                else
                {
                    text = "== {{int:filedesc}} ==\n" + text;

                    if (!text.Contains("{{" + GetCurrentLanguageCode() + "|"))
                        text = Regex.Replace(text, @"({{Information[\r\n]* *\| ?Description *= *)([^\r\n ][^\r\n]+)([\r\n])",
                            "$1{{" + GetCurrentLanguageCode() + "|1=$2}}$3", RegexOptions.IgnoreCase);

                    if (!LocalWikiData.LocalDomain.StartsWith("en.wikipedia"))  // speed boost - this is unneeded on enwiki
                    {
                        text = Regex.Replace(text, @"{{\s*" + LocalWikiData.Information, "{{Information", RegexOptions.IgnoreCase);
                        text = Regex.Replace(text, @"\|\s*" + LocalWikiData.Description + @"\s*=", "|Description    =", RegexOptions.IgnoreCase);
                        text = Regex.Replace(text, @"\|\s*" + LocalWikiData.Date + @"\s*=", "|Date           =", RegexOptions.IgnoreCase);
                        text = Regex.Replace(text, @"\|\s*" + LocalWikiData.Source + @"\s*=", "|Source         =", RegexOptions.IgnoreCase);
                        text = Regex.Replace(text, @"\|\s*" + LocalWikiData.Author + @"\s*=", "|Author         =", RegexOptions.IgnoreCase);
                        text = Regex.Replace(text, @"\|\s*" + LocalWikiData.Permission + @"\s*=", "|Permission     =", RegexOptions.IgnoreCase);
                        text = Regex.Replace(text, @"\|\s*" + LocalWikiData.Other_versions + @"\s*=", "|Other_versions =", RegexOptions.IgnoreCase);
                    }

                    //text = Regex.Replace(text, @"= *I .*created this (image|work) entirely by myself.?", "= {{own}} <!-- change this if not own work -->", RegexOptions.IgnoreCase);

                }

                text += "\n\n== {{Original upload log}} ==\n\n{{transferred from|" +
                    Settings.LocalDomain + "||[[:en:WP:FTCG|For the Common Good]]}} {{original description page|" +
                    Settings.LocalDomain + "|" + Uri.EscapeDataString(filename.Substring(filename.IndexOf(':') + 1).Replace(' ', '_')) + "}}";

                text += "\n\n{| class=\"wikitable\"\n! {{int:filehist-datetime}} !! {{int:filehist-dimensions}} !! {{int:filehist-user}} !! {{int:filehist-comment}}";
                foreach (XmlNode n in iis)
                {
                    text += "\n|-\n| " + FormatTimestamp(n) + " || " + FormatDimensions(n);
                    text += " || {{uv|" + n.Attributes["user"].Value + "|" + GetCurrentInterwikiPrefix(true) +
                        ":}} || ''<nowiki>(" +
                        n.Attributes["comment"].Value + ")</nowiki>''";
                }
                text += "\n|}";

                // remove multiple line breaks
                text = Regex.Replace(text, @"[\r\n]{3,}", "\n\n");

                Invoke(new Action(delegate()
                    {
                        textBox3.Text = text.Replace("\n", "\r\n");
                        lnkLocalFile.Enabled = lnkGoogleImageSearch.Enabled = true;
                        lnkCommonsFile.Enabled = false;
                        lblFileLinks.Visible = lstFileLinks.Visible = true;
                        if (lstFileLinks.ForeColor == SystemColors.GrayText)
                        {
                            lstFileLinks.Items.Clear();
                            lstFileLinks.Items.Add(Localization.GetString("Loading"));
                        }
                        EnableForm(true);
                    }));
            });
            sentry.Finally += new Action(delegate()
            {
                Invoke(new Action(delegate()
                {
                    EnableForm(true);
                }));
            });

            // download image file
            StringDictionary query = new StringDictionary
            {
                { "action", "query" },
                { "prop", "imageinfo" },
                { "iiprop", "comment|timestamp|user|url|size|mime|metadata" },
                { "iilimit", "500" },
                { "iiurlwidth", pictureBox1.Width.ToString() },
                { "iiurlheight", pictureBox1.Height.ToString() },
                { "titles", filename },
                { "redirects", "true" }
            };
            MorebitsDotNet.PostApi(Wiki.Local, query, delegate(XmlDocument doc)
            {
                switch (doc.GetElementsByTagName("page")[0].Attributes["imagerepository"].Value)
                {
                    case "shared":
                        MessageBox.Show(Localization.GetString("AlreadyCommons"));
                        sentry.Fail();
                        return;
                    case "":
                        if (doc.GetElementsByTagName("page")[0].Attributes["missing"] != null)
                            MessageBox.Show(Localization.GetString("ImageMissing"));
                        else
                            MessageBox.Show(Localization.GetString("NoFile"));
                        sentry.Fail();
                        return;
                }

                iis = doc.GetElementsByTagName("ii");

                Invoke(new Action(delegate()
                {
                    lblName.Text = doc.GetElementsByTagName("page")[0].Attributes["title"].Value;

                    if (iis.Count > 1)
                    {
                        lblPastRevisions.Visible = btnPastRevisions.Visible = true;
                        lblPastRevisions.Text = ((iis.Count == 2) ?
                            Localization.GetString("OneEarlierVersion_Label") :
                            Localization.GetString("EarlierVersions_Format", (iis.Count - 1).ToString()));
                    }
                    else
                        lblPastRevisions.Visible = btnPastRevisions.Visible = false;
                }));

                // download the file and display a thumbnail (also display metadata)
                DownloadFileAndDisplayThumb(iis[0]);

                sentry.DoneOne();
            }, ErrorHandler);

            // get wikitext of file description page
            query = new StringDictionary
            {
                { "action", "query" },
                { "prop", "revisions" },
                { "rvprop", "content" },
                { "titles", filename },
                { "redirects", "true" }
            };
            MorebitsDotNet.PostApi(Wiki.Local, query, delegate(XmlDocument doc)
            {
                if (doc.GetElementsByTagName("page")[0].Attributes["missing"] != null)
                {
                    // MessageBox.Show("Image does not exist on enwiki");    don't need to tell user twice
                    sentry.Fail();
                    return;
                }

                text = doc.GetElementsByTagName("rev")[0].InnerText;

                Invoke(new Action(delegate()
                {
                    textBox2.Text = text.Replace("\n", "\r\n");
                }));

                XmlNodeList ns = doc.GetElementsByTagName("n");
                if (ns.Count > 0)
                    Invoke(new Action(delegate()
                    {
                        filename = ns[0].Attributes["to"].Value;
                    }));
                Invoke(new Action(delegate()
                {
                    txtNormName.Text = Regex.Replace(filename, @"^\w+:", "File:");
                }));

                sentry.DoneOne();
            }, ErrorHandler);

            // get file links (not in sentry)
            query = new StringDictionary
            {
                { "action", "query" },
                { "list", "imageusage" },
                { "iulimit", "20" },
                { "iutitle", filename }
            };
            // prevent race conditions
            object current = new object();
            Invoke(new Action(delegate() { lstFileLinks.Tag = current; }));
            MorebitsDotNet.PostApi(Wiki.Local, query, delegate(XmlDocument doc)
            {
                if (lstFileLinks.Tag != current)
                    return;
                Invoke(new Action(delegate()
                {
                    lstFileLinks.Items.Clear();
                    XmlNodeList ius = doc.GetElementsByTagName("iu");
                    if (ius.Count == 0)
                    {
                        lstFileLinks.Items.Add(Localization.GetString("NoImageUsages_Label"));
                        return;
                    }

                    lstFileLinks.ForeColor = SystemColors.ControlText;
                    foreach (XmlNode i in ius)
                        lstFileLinks.Items.Add(i.Attributes["title"].Value);
                    if (doc.GetElementsByTagName("query-continue").Count > 0)
                        lstFileLinks.Items.Add("<<" + Localization.GetString("SeeWikiForFullList_Label") + ">>");
                }));
            }, ErrorHandler);
        }