DocumentationLink.h 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. // Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.
  2. #pragma once
  3. #include "CoreMinimal.h"
  4. #include "IDocumentation.h"
  5. #include "Misc/Paths.h"
  6. #include "HAL/FileManager.h"
  7. #include "Internationalization/Culture.h"
  8. #include "UnrealEdMisc.h"
  9. #include "Interfaces/IPluginManager.h"
  10. #include "DocumentationDefines.h"
  11. namespace EXT_DOC_NAMESPACE
  12. {
  13. class FExtDocumentationLink
  14. {
  15. public:
  16. static FString GetUrlRoot(const FString& BaseUrlId = FString())
  17. {
  18. return IDocumentation::Get()->GetBaseUrl(BaseUrlId);
  19. }
  20. static FString GetHomeUrl()
  21. {
  22. return GetHomeUrl(FInternationalization::Get().GetCurrentCulture());
  23. }
  24. static FString GetHomeUrl(const FCultureRef& Culture)
  25. {
  26. FString Url;
  27. FUnrealEdMisc::Get().GetURL( TEXT("UDNURL"), Url, true );
  28. Url.ReplaceInline(TEXT("/INT/"), *FString::Printf(TEXT("/%s/"), *(Culture->GetUnrealLegacyThreeLetterISOLanguageName())));
  29. return Url;
  30. }
  31. static FString ToUrl(const FString& Link, FDocumentationSourceInfo const& Source, const FString& BaseUrlId = FString())
  32. {
  33. return ToUrl(Link, FInternationalization::Get().GetCurrentCulture(), Source, BaseUrlId);
  34. }
  35. static FString ToUrl(const FString& Link, const FCultureRef& Culture, FDocumentationSourceInfo const& Source, const FString& BaseUrlId = FString())
  36. {
  37. // Get the base URL for this doc request, if any.
  38. // It may have any or all of the following pieces in it.
  39. FString UrlRoot = GetUrlRoot(BaseUrlId);
  40. FString RootPath;
  41. FString QueryString;
  42. FString Anchor;
  43. SplitLink(UrlRoot, RootPath, QueryString, Anchor);
  44. // Break up the incoming documentation link into pieces.
  45. // Depending on where it comes from, the link may be just a page ID, or it may have any or all
  46. // of the following pieces in it. If there's a query string, SplitLink will merge it with the existing query
  47. // string found in the base URL (if any). If there's an anchor, SplitLink will use it /instead/ of the
  48. // one in the BaseUrl (if any).
  49. FString LinkPath;
  50. SplitLink(Link, LinkPath, QueryString, Anchor);
  51. const FString TrimmedPartialPath = LinkPath.TrimChar('/');
  52. // Add query parameters for the source, if needed.
  53. AddSourceInfoToQueryString(QueryString, Source);
  54. FString AssembledUrl = RootPath + QueryString + Anchor;
  55. FUnrealEdMisc::Get().ReplaceDocumentationURLWildcards(AssembledUrl, Culture, TrimmedPartialPath);
  56. return AssembledUrl;
  57. }
  58. static FString ToFilePath( const FString& Link )
  59. {
  60. FInternationalization& I18N = FInternationalization::Get();
  61. FString FilePath = ToFilePath(Link, I18N.GetCurrentCulture());
  62. if (!FPaths::FileExists(FilePath))
  63. {
  64. const FCulturePtr FallbackCulture = I18N.GetCulture(TEXT("en"));
  65. if (FallbackCulture.IsValid())
  66. {
  67. const FString FallbackFilePath = ToFilePath(Link, FallbackCulture.ToSharedRef());
  68. if (FPaths::FileExists(FallbackFilePath))
  69. {
  70. FilePath = FallbackFilePath;
  71. }
  72. }
  73. }
  74. return FilePath;
  75. }
  76. static FString ToFilePath(const FString& Link, const FCultureRef& Culture)
  77. {
  78. FString Path;
  79. FString Anchor;
  80. FString QueryString;
  81. SplitLink(Link, Path, QueryString, Anchor);
  82. const FString PartialPath = FString::Printf(TEXT("%s%s/index.html"), *(Culture->GetUnrealLegacyThreeLetterISOLanguageName()), *Path);
  83. return FString::Printf(TEXT("%sDocumentation/HTML/%s"), *FPaths::ConvertRelativePathToFull(FPaths::EngineDir()), *PartialPath);
  84. }
  85. static FString ToFileUrl(const FString& Link, FDocumentationSourceInfo const& SourceInfo)
  86. {
  87. FInternationalization& I18N = FInternationalization::Get();
  88. FCultureRef Culture = I18N.GetCurrentCulture();
  89. FString FilePath = ToFilePath(Link, Culture);
  90. if (!FPaths::FileExists(FilePath))
  91. {
  92. const FCulturePtr FallbackCulture = I18N.GetCulture(TEXT("en"));
  93. if (FallbackCulture.IsValid())
  94. {
  95. const FString FallbackFilePath = ToFilePath(Link, FallbackCulture.ToSharedRef());
  96. if (FPaths::FileExists(FallbackFilePath))
  97. {
  98. Culture = FallbackCulture.ToSharedRef();
  99. }
  100. }
  101. }
  102. return ToFileUrl(Link, Culture, SourceInfo);
  103. }
  104. static FString ToFileUrl(const FString& Link, const FCultureRef& Culture, FDocumentationSourceInfo const& SourceInfo)
  105. {
  106. FString Path;
  107. FString Anchor;
  108. FString QueryString;
  109. SplitLink(Link, Path, QueryString, Anchor);
  110. AddSourceInfoToQueryString(QueryString, SourceInfo);
  111. return FString::Printf(TEXT("file:///%s%s%s"), *ToFilePath(Link, Culture), *QueryString, *Anchor);
  112. }
  113. static FString ToSourcePath(const FString& Link)
  114. {
  115. FInternationalization& I18N = FInternationalization::Get();
  116. FString SourcePath = ToSourcePath(Link, I18N.GetCurrentCulture());
  117. if (!FPaths::FileExists(SourcePath))
  118. {
  119. const FCulturePtr FallbackCulture = I18N.GetCulture(TEXT("en"));
  120. if (FallbackCulture.IsValid())
  121. {
  122. const FString FallbackSourcePath = ToSourcePath(Link, FallbackCulture.ToSharedRef());
  123. if (FPaths::FileExists(FallbackSourcePath))
  124. {
  125. SourcePath = FallbackSourcePath;
  126. }
  127. }
  128. }
  129. return SourcePath;
  130. }
  131. static FString ToSourcePath(const FString& Link, const FCultureRef& Culture)
  132. {
  133. FString Path;
  134. FString Anchor;
  135. FString QueryString;
  136. SplitLink(Link, Path, QueryString, Anchor);
  137. static FString DocumentationPath = IPluginManager::Get().FindPlugin(DocumentationHostPluginName)->GetBaseDir() / TEXT("Resources/Documentation/");
  138. const FString FullDirectoryPath = DocumentationPath + TEXT( "Source" ) + Path + "/";
  139. const FString WildCard = FString::Printf(TEXT("%s*.%s.udn"), *FullDirectoryPath, *(Culture->GetUnrealLegacyThreeLetterISOLanguageName()));
  140. TArray<FString> Filenames;
  141. IFileManager::Get().FindFiles(Filenames, *WildCard, true, false);
  142. if (Filenames.Num() > 0)
  143. {
  144. return FullDirectoryPath + Filenames[0];
  145. }
  146. // Since the source file doesn't exist already make up a valid name for a new one
  147. FString Category = FPaths::GetBaseFilename(Link);
  148. return FString::Printf(TEXT("%s%s.%s.udn"), *FullDirectoryPath, *Category, *(Culture->GetUnrealLegacyThreeLetterISOLanguageName()));
  149. }
  150. private:
  151. static void AddSourceInfoToQueryString(FString& QueryString, FDocumentationSourceInfo const& Info)
  152. {
  153. if (Info.IsEmpty() == false)
  154. {
  155. if (QueryString.IsEmpty())
  156. {
  157. QueryString = FString::Printf(TEXT("?utm_source=%s&utm_medium=%s&utm_campaign=%s"), *Info.Source, *Info.Medium, *Info.Campaign);
  158. }
  159. else
  160. {
  161. QueryString = FString::Printf(TEXT("%s&utm_source=%s&utm_medium=%s&utm_campaign=%s"), *QueryString, *Info.Source, *Info.Medium, *Info.Campaign);
  162. }
  163. }
  164. }
  165. static void SplitLink( const FString& Link, /*OUT*/ FString& Path, /*OUT*/ FString& QueryString, /*OUT*/ FString& Anchor )
  166. {
  167. FString CleanedLink = Link;
  168. CleanedLink.TrimStartAndEndInline();
  169. if ( CleanedLink == TEXT("%ROOT%") )
  170. {
  171. Path.Empty();
  172. Anchor.Empty();
  173. QueryString.Empty();
  174. }
  175. else
  176. {
  177. FString PathAndQueryString;
  178. if ( !CleanedLink.Split( TEXT("#"), &PathAndQueryString, &Anchor ) )
  179. {
  180. PathAndQueryString = CleanedLink;
  181. }
  182. else if ( !Anchor.IsEmpty() )
  183. {
  184. // ensure leading #
  185. Anchor = FString( TEXT("#") ) + Anchor;
  186. }
  187. if ( Anchor.EndsWith( TEXT("/") ) )
  188. {
  189. Anchor = Anchor.Left( Anchor.Len() - 1 );
  190. }
  191. if ( PathAndQueryString.EndsWith( TEXT("/") ) )
  192. {
  193. PathAndQueryString = PathAndQueryString.Left(PathAndQueryString.Len() - 1);
  194. }
  195. if ( !PathAndQueryString.IsEmpty() && !PathAndQueryString.StartsWith( TEXT("/") ) )
  196. {
  197. PathAndQueryString = FString(TEXT("/")) + PathAndQueryString;
  198. }
  199. // split path and query string
  200. if (!PathAndQueryString.Split(TEXT("?"), &Path, &QueryString))
  201. {
  202. Path = PathAndQueryString;
  203. }
  204. else if (!QueryString.IsEmpty())
  205. {
  206. // ensure leading ?
  207. QueryString = FString(TEXT("?")) + QueryString;
  208. }
  209. }
  210. }
  211. };
  212. }