| <?php |
| /******************************************************************************* |
| * Copyright (c) 2016, 2018 Eclipse Foundation and others. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0. |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| ********************************************************************************/ |
| |
| require_once dirname(__FILE__) . '/../classes/database.inc'; |
| require_once dirname(__FILE__) . '/../classes/common.php'; |
| require_once dirname(__FILE__) . '/../classes/debug.php'; |
| require_once dirname(__FILE__) . '/../classes/Project.class.php'; |
| |
| class CQ { |
| var $data; |
| var $name; |
| var $version; |
| |
| function __construct($data) { |
| $this->data = $data; |
| $summary = $data[0]['summary']; |
| if (preg_match('/(.+)\s+version:?\s*v?(\d+(?:\.\d+)+)/i', $summary, $matches)) { |
| $this->name = $matches[1]; |
| $this->version = $matches[2]; |
| } else if (preg_match('/(.+)\s+v?(\d+(?:\.\d+)+)/i', $summary, $matches)) { |
| $this->name = $matches[1]; |
| $this->version = $matches[2]; |
| } else { |
| $this->name = preg_replace('/\([^\)]*\)/', '', $summary); |
| if (preg_match('/(\d+(?:\.\d+)+)/i', $summary, $matches)) { |
| $this->version = $matches[0]; |
| } else { |
| $this->version = 'n/a'; |
| } |
| } |
| } |
| |
| function getId() { |
| return $this->data[0]['id']; |
| } |
| |
| function getSummary() { |
| return $this->data[0]['summary']; |
| } |
| |
| function getName() { |
| return $this->name; |
| } |
| |
| function getVersion() { |
| return $this->version; |
| } |
| |
| function getLicense() { |
| return $this->data[0]['license']; |
| } |
| |
| private function getComment($index = 0) { |
| return $this->data[0]['comment']; |
| } |
| |
| function getProjectUrl() { |
| $comment = $this->getComment(0); |
| if (preg_match('/^Project URL:\s*(\S+)$/m', $comment, $matches)) { |
| return trim($matches[1]); |
| } |
| if ($parent = $this->getParent()) |
| return $parent->getProjectUrl(); |
| } |
| |
| function getSourceUrl() { |
| $comment = $this->getComment(0); |
| if (preg_match('/^Source URL:\s*(\S+)$/m', $comment, $matches)) { |
| return trim($matches[1]); |
| } |
| if ($parent = $this->getParent()) |
| return $parent->getSourceUrl(); |
| } |
| |
| function getRoot() { |
| if ($parent = $this->getParent()) |
| return $parent->getRoot(); |
| return $this; |
| } |
| |
| function getParent() { |
| if (@$this->parent === null) { |
| $this->parent = $this->findParent(); |
| } |
| return $this->parent; |
| } |
| |
| private function findParent() { |
| if (!$id = $this->getParentId()) return FALSE; |
| $sql = " |
| select |
| b.bug_id as id, b.short_desc as summary, b.cf_license as license, l.thetext as comment |
| from bugs as b |
| join longdescs as l on b.bug_id=l.bug_id |
| where |
| b.bug_id=$id |
| order by b.bug_id, l.comment_id"; |
| if ($cqs = self::findCqs($sql)) return $cqs[0]; |
| return FALSE; |
| } |
| |
| public function getParentId() { |
| $summary = $this->getSummary(); |
| if (preg_match('/ATO\s+Orbit\s+(\d+)/', $summary, $matches)) |
| return $matches[1]; |
| |
| if (preg_match('/ATO\s*CQ\s*([0-9]+)/', $summary, $matches)) |
| return $matches[1]; |
| |
| if (preg_match('/PB\s*CQ\s*([0-9]+)/', $summary, $matches)) |
| return $matches[1]; |
| |
| if (preg_match('/PB\s*([0-9]+)/', $summary, $matches)) |
| return $matches[1]; |
| |
| if (preg_match('/Orbit\s*(?:CQ)?\s*([0-9]+)/', $summary, $matches)) |
| return $matches[1]; |
| |
| return null; |
| } |
| |
| public function __toString() { |
| return "{$this->getName()} ({$this->getVersion()})"; |
| } |
| /** |
| * Answers the approved third party CQs for a project. |
| * |
| * @param string $id A project id, e.g. 'technology.egit' |
| * @return CQ[] |
| */ |
| static function findCqs($id) { |
| $sql = " |
| select |
| b.bug_id as id, b.short_desc as summary, b.cf_license as license, l.thetext as comment |
| from bugs as b |
| join longdescs as l on b.bug_id=l.bug_id |
| join components as c on b.component_id=c.id |
| join keywords as kw on b.bug_id=kw.bug_id |
| join keyworddefs as kwd on kw.keywordid=kwd.id |
| where |
| b.bug_severity in ('approved', 'license_certified') |
| and kwd.name='thirdparty' |
| and c.name='$id' |
| order by b.short_desc"; |
| |
| $data = array(); |
| query('ipzilla', $sql, array(), function($row) use (&$data) { |
| $id = $row['id']; |
| $data[$id][] = $row; |
| }); |
| |
| $cqs = array(); |
| foreach($data as $id => $rows) { |
| $cqs[] = new CQ($rows); |
| } |
| |
| usort($cqs, function($a, $b) { |
| if (($compare = strcasecmp($a->getName(), $b->getName())) !== 0) { |
| return $compare; |
| } |
| return version_compare($a->getVersion(), $b->getVersion()); |
| }); |
| |
| return $cqs; |
| } |
| } |
| |
| /** |
| * Execute a function with the CQ for each piece of third |
| * party content leveraged by the project. The caller can |
| * provide an optional function to execute in the event that |
| * the project has no third party CQs. |
| * |
| * @param string $id The project id |
| * @param callable $function Function to execute once with each CQ. |
| * @param callable $else Function to execute if there are no CQs. |
| */ |
| function thirdPartyLibrariesDo($id, $function, $else = null) { |
| $cqs = CQ::findCqs ($id); |
| if ($cqs) foreach ($cqs as $cq ) call_user_func($function,$cq); |
| else if ($else) call_user_func($else); |
| } |
| |
| function getDefaultFileHeader($licenses, $width=80) { |
| return renderAsHeaderComment(License::getDefaultFileHeader($licenses), $width); |
| } |
| |
| function getAlternativeFileHeader($licenses, $width=80) { |
| return renderAsHeaderComment(License::getAlternativeFileHeader($licenses), $width); |
| } |
| |
| function renderNoticeFile($id, $width=80) { |
| generateNoticeFileContent($id, new PlainTextRender($width)); |
| } |
| |
| function generateNoticeFileContent($id, $render) { |
| $project = Project::getProject($id); |
| |
| $render |
| ->title("Notices for " . $project->getFormalName()) |
| ->paragraph( |
| "This content is produced and maintained by the |
| {$project->getFormalName()} project.") |
| ->unorderedList(array("Project home: {$project->getUrl()}")) |
| ->section("Trademarks") |
| ->paragraph(getTrademarksStatement($project)) |
| ->section("Copyright") |
| ->paragraph("All content is the property of the respective authors " |
| ."or their employers. For more information regarding authorship " |
| ."of content, please consult the listed source code repository logs.") |
| ->section("Declared Project Licenses") |
| ->paragraph(valueOrDefaultWhenEmpty(License::getLicensesStatement($project->getLicenses()), "[Provide license information here]")) |
| ->paragraph("SPDX-License-Identifier: " . valueOrDefaultWhenEmpty($project->getSPDXLicenseExpression(), "[Provide SPDX expression here]")) |
| ->section("Source Code") |
| ->paragraph("The project maintains the following source code repositories:") |
| ->unorderedList($project->getSourceRepositories(), function($repository) {return $repository->getUrl();}); |
| |
| $render |
| ->section("Third-party Content") |
| ->paragraph("This project leverages the following third party content."); |
| thirdPartyLibrariesDo($id, function($cq) use (&$render) { |
| $properties = array(); |
| if ($cq->getLicense()) $properties[] = "License: {$cq->getLicense()}"; |
| if ($cq->getProjectUrl()) $properties[] = "Project: {$cq->getProjectUrl()}"; |
| if ($cq->getSourceUrl()) $properties[] = "Source: {$cq->getSourceUrl()}"; |
| |
| $render |
| ->paragraph("{$cq->getName()} ({$cq->getVersion()})", 3) |
| ->unorderedList($properties); |
| }, function() use (&$render) { |
| $render->paragraph("None"); |
| }); |
| |
| $render |
| ->section("Cryptography") |
| ->paragraph( |
| "Content may contain encryption software. |
| The country in which you are currently may have restrictions on |
| the import, possession, and use, and/or re-export to another country, |
| of encryption software. BEFORE using any encryption software, please |
| check the country's laws, regulations and policies concerning the import, |
| possession, or use, and re-export of encryption software, to see if |
| this is permitted.") |
| ->end(); |
| } |
| |
| function getTrademarksStatement(Project $project) { |
| $trademarks = array(); |
| if ($unregistered = $project->getTrademarks(TRADEMARK_UNREGISTERED, true)) { |
| $content = implodeWithConjunction($unregistered, 'and'); |
| $content .= count($unregistered) == 1 ? ' is a trademark' : ' are trademarks'; |
| $content .= ' of the Eclipse Foundation.'; |
| $trademarks[] = $content; |
| } |
| if ($registered = $project->getTrademarks(TRADEMARK_REGISTERED, true)) { |
| $content = implodeWithConjunction($registered, 'and'); |
| $content .= count($registered) == 1 ? ' is a registered trademark' : ' are registered trademarks'; |
| $content .= ' of the Eclipse Foundation.'; |
| $trademarks[] = $content; |
| } |
| if ($trademarks) { |
| return join(' ', $trademarks); |
| } |
| return "No trademarks."; |
| } |
| |
| function renderContributingFile($id, $width=80) { |
| generateContributingFileContent($id, new PlainTextRender($width)); |
| } |
| |
| function generateContributingFileContent($id, $render) { |
| $project = Project::getProject($id); |
| $forge = $project->getForge(); |
| |
| $render |
| ->title("Contributing to " . $project->getFormalName()) |
| ->paragraph("Thanks for your interest in this project.") |
| |
| ->section("Project description") |
| ->paragraph(trim(html_entity_decode(strip_tags($project->getDescription())))) |
| ->unorderedList(array($project->getUrl())) |
| |
| ->section("Developer resources") |
| ->paragraph("Information regarding source code management, builds, coding standards, and more.") |
| ->unorderedList(array($project->getUrl() . "/developer")) |
| ->paragraph("The project maintains the following source code repositories") |
| ->unorderedList($project->getSourceRepositories(), |
| function($each) { |
| return $each->getUrl(); |
| }); |
| |
| if ($product = $project->getBugzillaProduct()) { |
| if ($forge->isEclipseForge()) { |
| $base = 'https://bugs.eclipse.org/bugs'; |
| } else { |
| $base = "https://{$forge->getId()}.org/bugs"; |
| } |
| $search = "$base/buglist.cgi?product=$product"; |
| $create = "$base/enter_bug.cgi?product=$product"; |
| |
| if ($components = $project->getBugzillaComponents()) { |
| $search .= "&" . $components[1]; |
| $create .= "&" . $components[1]; |
| } |
| |
| $render |
| ->paragraph("This project uses Bugzilla to track ongoing development and issues.") |
| ->unorderedList( |
| array( |
| "Search for issues: {$search}", |
| "Create a new report: {$create}") |
| ) |
| ->paragraph("Be sure to search for existing bugs before you create another one." |
| ." Remember that contributions are always welcome!"); |
| } |
| |
| $links = array(); |
| // @formatter:off |
| $render |
| ->section("Eclipse Development Process") |
| ->paragraph("This Eclipse Foundation open project is governed by the |
| Eclipse Foundation Development Process and operates under the terms |
| of the Eclipse IP Policy."); |
| $links[] = "https://eclipse.org/projects/dev_process"; |
| $links[] = "https://www.eclipse.org/org/documents/Eclipse_IP_Policy.pdf"; |
| |
| // @formatter:on |
| // We include a statement regarding contributions under the specification |
| // process when the project is involved in specification work. For now, |
| // we consider a project to be under the specification process if the |
| // project is a specification project. This should be more generally |
| // expanded to include projects that implement specifications. We don't |
| // track this in an easily queriable manner. Ideally, I believe, we should |
| // include projects that are of interest to a working group that implements |
| // a specification process. e.g. (in Java to be concise), |
| // |
| // project.getWorkingGroups().findAny(wg -> wg.hasSpecificationProjects()) |
| // |
| if ($project->isSpecificationProject()) { |
| // FIXME Hack: hard coded content specific to Jakarta EE. |
| if (strcmp('Jakarta EE', $project->getSpecificationWorkingGroupName()) == 0) { |
| // @formatter:off |
| $render |
| ->paragraph( |
| "The Jakarta EE Specification Committee has adopted the Jakarta EE |
| Specification Process (JESP) in accordance with the Eclipse Foundation |
| Specification Process v1.2 (EFSP) to ensure that the specification process |
| is complied with by all Jakarta EE specification projects."); |
| // @formatter:on |
| $links[] = "https://jakarta.ee/about/jesp/"; |
| } else { |
| // @formatter:off |
| $render |
| ->section("Specifications") |
| ->paragraph( |
| "This specification project operates under the terms of the Eclipse Foundation |
| Specification process."); |
| // @formatter:on |
| $links[] = "https://www.eclipse.org/projects/efsp/"; |
| } |
| $links[] = "https://www.eclipse.org/legal/efsp_non_assert.php"; |
| } |
| $render->unorderedList($links); |
| |
| $render |
| ->section("Eclipse Contributor Agreement") |
| ->paragraph( |
| "In order to be able to contribute to Eclipse Foundation projects |
| you must electronically sign the Eclipse Contributor Agreement (ECA).") |
| ->unorderedList(array("http://www.eclipse.org/legal/ECA.php")) |
| ->paragraph( |
| "The ECA provides the Eclipse Foundation with a permanent record |
| that you agree that each of your contributions will comply with |
| the commitments documented in the Developer Certificate of Origin (DCO). |
| Having an ECA on file associated with the email address matching the |
| \"Author\" field of your contribution's Git commits fulfills the |
| DCO's requirement that you sign-off on your contributions.") |
| ->paragraph( |
| "For more information, please see the Eclipse Committer Handbook: |
| https://www.eclipse.org/projects/handbook/#resources-commit") |
| |
| ->section("Contact") |
| ->paragraph("Contact the project developers via the project's \"dev\" list.") |
| ->unorderedList(array($project->getDevListUrl())) |
| |
| ->end(); |
| } |
| |
| /** |
| * A very simple class for rendering structured content |
| * in plaintext. |
| */ |
| class PlainTextRender { |
| var $width; |
| |
| function __construct($width) { |
| $this->width = $width; |
| } |
| |
| function title($text) { |
| $this->section($text, 1); |
| return $this; |
| } |
| |
| function section($text, $level=2) { |
| for($index=0;$index<$level;$index++) echo "#"; |
| echo " "; |
| echo $text; |
| echo "\n\n"; |
| return $this; |
| } |
| |
| function paragraph($text, $alternate = '') { |
| echo wordwrap($this->normalize($text != null ? $text : $alternate), $this->width); |
| echo "\n\n"; |
| return $this; |
| } |
| |
| private function normalize($text) { |
| return preg_replace('/\s+/', ' ', $text); |
| } |
| |
| function unorderedList($values, $each = 'doNothing') { |
| foreach($values as $value) { |
| echo "* "; |
| $lines = explode("\n",wordwrap($each($value), $this->width-3)); |
| echo implode("\n ", $lines); |
| echo "\n"; |
| } |
| echo "\n"; |
| return $this; |
| } |
| |
| function end() {} |
| } |
| |
| function doNothing($value) { |
| return $value; |
| } |
| |
| function valueOrDefaultWhenEmpty($value, $default) { |
| return !empty($value) ? $value : $default; |
| } |