An updated theme (based on Atticus Finch) for ClassicPress
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

243 lines
6.2 KiB

3 years ago
  1. <?php
  2. if ( !class_exists('Puc_v4p1_Vcs_Api') ):
  3. abstract class Puc_v4p1_Vcs_Api {
  4. protected $tagNameProperty = 'name';
  5. /**
  6. * @var string
  7. */
  8. protected $repositoryUrl = '';
  9. /**
  10. * @var mixed Authentication details for private repositories. Format depends on service.
  11. */
  12. protected $credentials = null;
  13. /**
  14. * @var string The filter tag that's used to filter options passed to wp_remote_get.
  15. * For example, "puc_request_info_options-slug" or "puc_request_update_options_theme-slug".
  16. */
  17. protected $httpFilterName = '';
  18. /**
  19. * Puc_v4p1_Vcs_Api constructor.
  20. *
  21. * @param string $repositoryUrl
  22. * @param array|string|null $credentials
  23. */
  24. public function __construct($repositoryUrl, $credentials = null) {
  25. $this->repositoryUrl = $repositoryUrl;
  26. $this->setAuthentication($credentials);
  27. }
  28. /**
  29. * @return string
  30. */
  31. public function getRepositoryUrl() {
  32. return $this->repositoryUrl;
  33. }
  34. /**
  35. * Figure out which reference (i.e tag or branch) contains the latest version.
  36. *
  37. * @param string $configBranch Start looking in this branch.
  38. * @return null|Puc_v4p1_Vcs_Reference
  39. */
  40. abstract public function chooseReference($configBranch);
  41. /**
  42. * Get the readme.txt file from the remote repository and parse it
  43. * according to the plugin readme standard.
  44. *
  45. * @param string $ref Tag or branch name.
  46. * @return array Parsed readme.
  47. */
  48. public function getRemoteReadme($ref = 'master') {
  49. $fileContents = $this->getRemoteFile('readme.txt', $ref);
  50. if ( empty($fileContents) ) {
  51. return array();
  52. }
  53. $parser = new PucReadmeParser();
  54. return $parser->parse_readme_contents($fileContents);
  55. }
  56. /**
  57. * Get a branch.
  58. *
  59. * @param string $branchName
  60. * @return Puc_v4p1_Vcs_Reference|null
  61. */
  62. abstract public function getBranch($branchName);
  63. /**
  64. * Get a specific tag.
  65. *
  66. * @param string $tagName
  67. * @return Puc_v4p1_Vcs_Reference|null
  68. */
  69. abstract public function getTag($tagName);
  70. /**
  71. * Get the tag that looks like the highest version number.
  72. * (Implementations should skip pre-release versions if possible.)
  73. *
  74. * @return Puc_v4p1_Vcs_Reference|null
  75. */
  76. abstract public function getLatestTag();
  77. /**
  78. * Check if a tag name string looks like a version number.
  79. *
  80. * @param string $name
  81. * @return bool
  82. */
  83. protected function looksLikeVersion($name) {
  84. //Tag names may be prefixed with "v", e.g. "v1.2.3".
  85. $name = ltrim($name, 'v');
  86. //The version string must start with a number.
  87. if ( !is_numeric(substr($name, 0, 1)) ) {
  88. return false;
  89. }
  90. //The goal is to accept any SemVer-compatible or "PHP-standardized" version number.
  91. return (preg_match('@^(\d{1,5}?)(\.\d{1,10}?){0,4}?($|[abrdp+_\-]|\s)@i', $name) === 1);
  92. }
  93. /**
  94. * Check if a tag appears to be named like a version number.
  95. *
  96. * @param stdClass $tag
  97. * @return bool
  98. */
  99. protected function isVersionTag($tag) {
  100. $property = $this->tagNameProperty;
  101. return isset($tag->$property) && $this->looksLikeVersion($tag->$property);
  102. }
  103. /**
  104. * Sort a list of tags as if they were version numbers.
  105. * Tags that don't look like version number will be removed.
  106. *
  107. * @param stdClass[] $tags Array of tag objects.
  108. * @return stdClass[] Filtered array of tags sorted in descending order.
  109. */
  110. protected function sortTagsByVersion($tags) {
  111. //Keep only those tags that look like version numbers.
  112. $versionTags = array_filter($tags, array($this, 'isVersionTag'));
  113. //Sort them in descending order.
  114. usort($versionTags, array($this, 'compareTagNames'));
  115. return $versionTags;
  116. }
  117. /**
  118. * Compare two tags as if they were version number.
  119. *
  120. * @param stdClass $tag1 Tag object.
  121. * @param stdClass $tag2 Another tag object.
  122. * @return int
  123. */
  124. protected function compareTagNames($tag1, $tag2) {
  125. $property = $this->tagNameProperty;
  126. if ( !isset($tag1->$property) ) {
  127. return 1;
  128. }
  129. if ( !isset($tag2->$property) ) {
  130. return -1;
  131. }
  132. return -version_compare(ltrim($tag1->$property, 'v'), ltrim($tag2->$property, 'v'));
  133. }
  134. /**
  135. * Get the contents of a file from a specific branch or tag.
  136. *
  137. * @param string $path File name.
  138. * @param string $ref
  139. * @return null|string Either the contents of the file, or null if the file doesn't exist or there's an error.
  140. */
  141. abstract public function getRemoteFile($path, $ref = 'master');
  142. /**
  143. * Get the timestamp of the latest commit that changed the specified branch or tag.
  144. *
  145. * @param string $ref Reference name (e.g. branch or tag).
  146. * @return string|null
  147. */
  148. abstract public function getLatestCommitTime($ref);
  149. /**
  150. * Get the contents of the changelog file from the repository.
  151. *
  152. * @param string $ref
  153. * @param string $localDirectory Full path to the local plugin or theme directory.
  154. * @return null|string The HTML contents of the changelog.
  155. */
  156. public function getRemoteChangelog($ref, $localDirectory) {
  157. $filename = $this->findChangelogName($localDirectory);
  158. if ( empty($filename) ) {
  159. return null;
  160. }
  161. $changelog = $this->getRemoteFile($filename, $ref);
  162. if ( $changelog === null ) {
  163. return null;
  164. }
  165. /** @noinspection PhpUndefinedClassInspection */
  166. return Parsedown::instance()->text($changelog);
  167. }
  168. /**
  169. * Guess the name of the changelog file.
  170. *
  171. * @param string $directory
  172. * @return string|null
  173. */
  174. protected function findChangelogName($directory) {
  175. if ( empty($directory) || !is_dir($directory) || ($directory === '.') ) {
  176. return null;
  177. }
  178. $possibleNames = array('CHANGES.md', 'CHANGELOG.md', 'changes.md', 'changelog.md');
  179. $files = scandir($directory);
  180. $foundNames = array_intersect($possibleNames, $files);
  181. if ( !empty($foundNames) ) {
  182. return reset($foundNames);
  183. }
  184. return null;
  185. }
  186. /**
  187. * Set authentication credentials.
  188. *
  189. * @param $credentials
  190. */
  191. public function setAuthentication($credentials) {
  192. $this->credentials = $credentials;
  193. }
  194. public function isAuthenticationEnabled() {
  195. return !empty($this->credentials);
  196. }
  197. /**
  198. * @param string $url
  199. * @return string
  200. */
  201. public function signDownloadUrl($url) {
  202. return $url;
  203. }
  204. /**
  205. * @param string $filterName
  206. */
  207. public function setHttpFilterName($filterName) {
  208. $this->httpFilterName = $filterName;
  209. }
  210. }
  211. endif;