FeedController.php 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. <?php
  2. /*
  3. * This file is part of Packagist.
  4. *
  5. * (c) Jordi Boggiano <j.boggiano@seld.be>
  6. * Nils Adermann <naderman@naderman.de>
  7. *
  8. * For the full copyright and license information, please view the LICENSE
  9. * file that was distributed with this source code.
  10. */
  11. namespace Packagist\WebBundle\Controller;
  12. use Doctrine\ORM\QueryBuilder;
  13. use Packagist\WebBundle\Entity\Package;
  14. use Packagist\WebBundle\Entity\Version;
  15. use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
  16. use Symfony\Component\HttpFoundation\Request;
  17. use Symfony\Component\HttpFoundation\Response;
  18. use Symfony\Component\Routing\Annotation\Route;
  19. use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
  20. use Zend\Feed\Writer\Entry;
  21. use Zend\Feed\Writer\Feed;
  22. /**
  23. * @author Rafael Dohms <rafael@doh.ms>
  24. *
  25. * @Route("/feeds")
  26. */
  27. class FeedController extends Controller
  28. {
  29. /**
  30. * @Route("/", name="feeds")
  31. * @Template
  32. */
  33. public function feedsAction()
  34. {
  35. return array();
  36. }
  37. /**
  38. * @Route(
  39. * "/packages.{_format}",
  40. * name="feed_packages",
  41. * requirements={"_format"="(rss|atom)"},
  42. * methods={"GET"}
  43. * )
  44. */
  45. public function packagesAction(Request $req)
  46. {
  47. /** @var $repo \Packagist\WebBundle\Entity\PackageRepository */
  48. $repo = $this->getDoctrine()->getRepository('PackagistWebBundle:Package');
  49. $packages = $this->getLimitedResults(
  50. $repo->getQueryBuilderForNewestPackages()
  51. );
  52. $feed = $this->buildFeed(
  53. $req,
  54. 'Newly Submitted Packages',
  55. 'Latest packages submitted to Packagist.',
  56. $this->generateUrl('browse', array(), UrlGeneratorInterface::ABSOLUTE_URL),
  57. $packages
  58. );
  59. return $this->buildResponse($req, $feed);
  60. }
  61. /**
  62. * @Route(
  63. * "/releases.{_format}",
  64. * name="feed_releases",
  65. * requirements={"_format"="(rss|atom)"},
  66. * methods={"GET"}
  67. * )
  68. */
  69. public function releasesAction(Request $req)
  70. {
  71. /** @var $repo \Packagist\WebBundle\Entity\VersionRepository */
  72. $repo = $this->getDoctrine()->getRepository('PackagistWebBundle:Version');
  73. $packages = $this->getLimitedResults(
  74. $repo->getQueryBuilderForLatestVersionWithPackage()
  75. );
  76. $feed = $this->buildFeed(
  77. $req,
  78. 'New Releases',
  79. 'Latest releases of all packages.',
  80. $this->generateUrl('browse', array(), UrlGeneratorInterface::ABSOLUTE_URL),
  81. $packages
  82. );
  83. return $this->buildResponse($req, $feed);
  84. }
  85. /**
  86. * @Route(
  87. * "/vendor.{vendor}.{_format}",
  88. * name="feed_vendor",
  89. * requirements={"_format"="(rss|atom)", "vendor"="[A-Za-z0-9_.-]+"},
  90. * methods={"GET"}
  91. * )
  92. */
  93. public function vendorAction(Request $req, $vendor)
  94. {
  95. /** @var $repo \Packagist\WebBundle\Entity\VersionRepository */
  96. $repo = $this->getDoctrine()->getRepository('PackagistWebBundle:Version');
  97. $packages = $this->getLimitedResults(
  98. $repo->getQueryBuilderForLatestVersionWithPackage($vendor)
  99. );
  100. $feed = $this->buildFeed(
  101. $req,
  102. "$vendor packages",
  103. "Latest packages updated on Packagist of $vendor.",
  104. $this->generateUrl('view_vendor', array('vendor' => $vendor), UrlGeneratorInterface::ABSOLUTE_URL),
  105. $packages
  106. );
  107. return $this->buildResponse($req, $feed);
  108. }
  109. /**
  110. * @Route(
  111. * "/package.{package}.{_format}",
  112. * name="feed_package",
  113. * requirements={"_format"="(rss|atom)", "package"="[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+"},
  114. * methods={"GET"}
  115. * )
  116. */
  117. public function packageAction(Request $req, $package)
  118. {
  119. /** @var $repo \Packagist\WebBundle\Entity\VersionRepository */
  120. $repo = $this->getDoctrine()->getRepository('PackagistWebBundle:Version');
  121. $packages = $this->getLimitedResults(
  122. $repo->getQueryBuilderForLatestVersionWithPackage(null, $package)
  123. );
  124. $feed = $this->buildFeed(
  125. $req,
  126. "$package releases",
  127. "Latest releases on Packagist of $package.",
  128. $this->generateUrl('view_package', array('name' => $package), UrlGeneratorInterface::ABSOLUTE_URL),
  129. $packages
  130. );
  131. $response = $this->buildResponse($req, $feed);
  132. $first = reset($packages);
  133. if (false !== $first) {
  134. $response->setDate($first->getReleasedAt());
  135. }
  136. return $response;
  137. }
  138. /**
  139. * Limits a query to the desired number of results
  140. *
  141. * @param \Doctrine\ORM\QueryBuilder $queryBuilder
  142. *
  143. * @return array|\Traversable
  144. */
  145. protected function getLimitedResults(QueryBuilder $queryBuilder)
  146. {
  147. $query = $queryBuilder
  148. ->getQuery()
  149. ->setMaxResults(
  150. $this->container->getParameter('packagist_web.rss_max_items')
  151. );
  152. return $query->getResult();
  153. }
  154. /**
  155. * Builds the desired feed
  156. *
  157. * @param string $title
  158. * @param string $description
  159. * @param array $items
  160. *
  161. * @return \Zend\Feed\Writer\Feed
  162. */
  163. protected function buildFeed(Request $req, $title, $description, $url, $items)
  164. {
  165. $feed = new Feed();
  166. $feed->setTitle($title);
  167. $feed->setDescription($description);
  168. $feed->setLink($url);
  169. $feed->setGenerator('Packagist');
  170. foreach ($items as $item) {
  171. $entry = $feed->createEntry();
  172. $this->populateEntry($entry, $item);
  173. $feed->addEntry($entry);
  174. }
  175. if ($req->getRequestFormat() == 'atom') {
  176. $feed->setFeedLink(
  177. $req->getUri(),
  178. $req->getRequestFormat()
  179. );
  180. }
  181. if ($feed->count()) {
  182. $feed->setDateModified($feed->getEntry(0)->getDateModified());
  183. } else {
  184. $feed->setDateModified(new \DateTime());
  185. }
  186. return $feed;
  187. }
  188. /**
  189. * Receives either a Package or a Version and populates a feed entry.
  190. *
  191. * @param \Zend\Feed\Writer\Entry $entry
  192. * @param Package|Version $item
  193. */
  194. protected function populateEntry(Entry $entry, $item)
  195. {
  196. if ($item instanceof Package) {
  197. $this->populatePackageData($entry, $item);
  198. } elseif ($item instanceof Version) {
  199. $this->populatePackageData($entry, $item->getPackage());
  200. $this->populateVersionData($entry, $item);
  201. }
  202. }
  203. /**
  204. * Populates a feed entry with data coming from Package objects.
  205. *
  206. * @param \Zend\Feed\Writer\Entry $entry
  207. * @param Package $package
  208. */
  209. protected function populatePackageData(Entry $entry, Package $package)
  210. {
  211. $entry->setTitle($package->getName());
  212. $entry->setLink(
  213. $this->generateUrl(
  214. 'view_package',
  215. array('name' => $package->getName()),
  216. UrlGeneratorInterface::ABSOLUTE_URL
  217. )
  218. );
  219. $entry->setId($package->getName());
  220. $entry->setDateModified($package->getCreatedAt());
  221. $entry->setDateCreated($package->getCreatedAt());
  222. $entry->setDescription($package->getDescription() ?: ' ');
  223. }
  224. /**
  225. * Populates a feed entry with data coming from Version objects.
  226. *
  227. * @param \Zend\Feed\Writer\Entry $entry
  228. * @param Version $version
  229. */
  230. protected function populateVersionData(Entry $entry, Version $version)
  231. {
  232. $entry->setTitle($entry->getTitle()." ({$version->getVersion()})");
  233. $entry->setId($entry->getId().' '.$version->getVersion());
  234. $entry->setDateModified($version->getReleasedAt());
  235. $entry->setDateCreated($version->getReleasedAt());
  236. foreach ($version->getAuthors() as $author) {
  237. /** @var $author \Packagist\WebBundle\Entity\Author */
  238. if ($author->getName()) {
  239. $entry->addAuthor(array(
  240. 'name' => $author->getName()
  241. ));
  242. }
  243. }
  244. }
  245. /**
  246. * Creates a HTTP Response and exports feed
  247. *
  248. * @param \Zend\Feed\Writer\Feed $feed
  249. *
  250. * @return \Symfony\Component\HttpFoundation\Response
  251. */
  252. protected function buildResponse(Request $req, Feed $feed)
  253. {
  254. $content = $feed->export($req->getRequestFormat());
  255. $response = new Response($content, 200);
  256. $response->setSharedMaxAge(3600);
  257. return $response;
  258. }
  259. }