| Index: sitescripts/extensions/bin/updateDownloadLinks.py | 
| =================================================================== | 
| --- a/sitescripts/extensions/bin/updateDownloadLinks.py | 
| +++ b/sitescripts/extensions/bin/updateDownloadLinks.py | 
| @@ -29,6 +29,8 @@ | 
| from StringIO import StringIO | 
| from sitescripts.utils import get_config, get_template | 
| from sitescripts.extensions.utils import compareVersions, Configuration, getSafariCertificateID | 
| +from sitescripts.extensions.android import get_min_sdk_version | 
| +from sitescripts.extensions.pad import PadFile | 
| from buildtools.packagerGecko import KNOWN_APPS | 
|  | 
| def urlencode(value): | 
| @@ -66,84 +68,78 @@ | 
| """ | 
| gets download link for a Chrome add-on from the Chrome Gallery site | 
| """ | 
| -  param = 'id=%s&uc' % urlencode(galleryID) | 
| -  url = 'https://clients2.google.com/service/update2/crx?x=%s' % urlencode(param) | 
| -  contents = urlopen(url).read() | 
| -  document = dom.parseString(contents) | 
| +  galleryID = urlencode(galleryID) | 
| + | 
| +  url = 'https://clients2.google.com/service/update2/crx?x=' | 
| +  url += urlencode('id=%s&uc' % galleryID) | 
| +  document = dom.parse(urlopen(url)) | 
| updateTags = document.getElementsByTagName('updatecheck') | 
| -  updateTag = updateTags[0] if len(updateTags) > 0 else None | 
| -  if updateTag and updateTag.hasAttribute('codebase') and updateTag.hasAttribute('version'): | 
| -    return (updateTag.getAttribute('codebase'), updateTag.getAttribute('version')) | 
| -  else: | 
| +  version = updateTags and updateTags[0].getAttribute('version') | 
| + | 
| +  if not version: | 
| return (None, None) | 
|  | 
| +  request = urllib2.Request('https://chrome.google.com/webstore/detail/_/' + galleryID) | 
| +  request.get_method = lambda : 'HEAD' | 
| +  url = urllib2.urlopen(request).geturl() | 
| + | 
| +  return (url, version) | 
| + | 
| def getOperaDownloadLink(galleryID): | 
| """ | 
| gets download link for an Opera add-on from the Opera Addons site | 
| """ | 
| -  class HeadRequest(urllib2.Request): | 
| -    def get_method(self): | 
| -      return "HEAD" | 
| +  galleryID = urlencode(galleryID) | 
|  | 
| -  url = 'https://addons.opera.com/extensions/download/%s/' % urlencode(galleryID) | 
| -  response = urllib2.urlopen(HeadRequest(url)) | 
| -  content_disposition = response.info().dict.get('content-disposition', None) | 
| -  if content_disposition != None: | 
| -    match = re.search(r'filename=\S+-([\d.]+)-\d+\.oex$', content_disposition) | 
| -  else: | 
| -    match = None; | 
| -  if match: | 
| -    return (url, match.group(1)) | 
| -  else: | 
| -    return (None, None) | 
| +  request = urllib2.Request('https://addons.opera.com/extensions/download/%s/' % galleryID) | 
| +  request.get_method = lambda : 'HEAD' | 
| +  response = urllib2.urlopen(request) | 
| + | 
| +  content_disposition = response.info().getheader('Content-Disposition') | 
| +  if content_disposition: | 
| +    match = re.search(r'filename=\S+-([\d.]+)-\d+\.crx$', content_disposition) | 
| +    if match: | 
| +      return ('https://addons.opera.com/extensions/details/%s/' % galleryID , match.group(1)) | 
| + | 
| +  return (None, None) | 
|  | 
| def getLocalLink(repo): | 
| """ | 
| gets the link for the newest download of an add-on in the local downloads | 
| repository | 
| """ | 
| -  url = repo.downloadsURL | 
| - | 
| highestURL = None | 
| highestVersion = None | 
|  | 
| -  if repo.type in ('gecko', 'chrome', 'opera', 'safari'): | 
| -    prefix = readRawMetadata(repo).get('general', 'basename') | 
| -  else: | 
| -    prefix = os.path.basename(repo.repository) | 
| -  prefix += '-' | 
| -  suffix = repo.packageSuffix | 
| +  for filename, version in repo.getDownloads(): | 
| +    if not highestVersion or compareVersions(version, highestVersion) > 0: | 
| +      highestURL = urlparse.urljoin(repo.downloadsURL, filename) | 
| +      highestVersion = version | 
|  | 
| -  # go through the downloads repository looking for downloads matching this extension | 
| -  command = ['hg', 'locate', '-R', repo.downloadsRepo, '-r', 'default'] | 
| -  result = subprocess.check_output(command) | 
| -  for fileName in result.splitlines(): | 
| -    if fileName.startswith(prefix) and fileName.endswith(suffix): | 
| -      version = fileName[len(prefix):len(fileName) - len(suffix)] | 
| -      if highestVersion == None or compareVersions(version, highestVersion) > 0: | 
| -        highestURL = urlparse.urljoin(url, fileName) | 
| -        highestVersion = version | 
| return (highestURL, highestVersion) | 
|  | 
| def getDownloadLink(repo): | 
| """ | 
| gets the download link to the most current version of an extension | 
| """ | 
| -  galleryURL = None | 
| -  galleryVersion = None | 
| -  if repo.type == "gecko" and repo.galleryID: | 
| +  # you can't easily install extensions from third-party sources on Chrome | 
| +  # and Opera. So always get the link for the version on the Web Store. | 
| +  if repo.galleryID: | 
| +    if repo.type == "chrome": | 
| +      return getGoogleDownloadLink(repo.galleryID) | 
| +    if repo.type == "opera": | 
| +      return getOperaDownloadLink(repo.galleryID) | 
| + | 
| +  (localURL, localVersion) = getLocalLink(repo) | 
| + | 
| +  # get a link to Firefox Add-Ons, if the latest version has been published there | 
| +  if repo.type == 'gecko' and repo.galleryID: | 
| (galleryURL, galleryVersion) = getMozillaDownloadLink(repo.galleryID) | 
| -  elif repo.type == "chrome" and repo.galleryID: | 
| -    (galleryURL, galleryVersion) = getGoogleDownloadLink(repo.galleryID) | 
| -  elif repo.type == "opera" and repo.galleryID: | 
| -    (galleryURL, galleryVersion) = getOperaDownloadLink(repo.galleryID) | 
| +    if not localVersion or (galleryVersion and | 
| +                            compareVersions(galleryVersion, localVersion) >= 0): | 
| +      return (galleryURL, galleryVersion) | 
|  | 
| -  (downloadsURL, downloadsVersion) = getLocalLink(repo) | 
| -  if galleryVersion == None or (downloadsVersion != None and | 
| -                                compareVersions(galleryVersion, downloadsVersion) < 0): | 
| -    return (downloadsURL, downloadsVersion) | 
| -  else: | 
| -    return (galleryURL, galleryVersion) | 
| +  return (localURL, localVersion) | 
|  | 
| def getQRCode(text): | 
| try: | 
| @@ -175,23 +171,6 @@ | 
| if qrcode != None: | 
| result.set(repo.repositoryName, "qrcode", qrcode) | 
|  | 
| -def readRawMetadata(repo, version='tip'): | 
| -  files = subprocess.check_output(['hg', '-R', repo.repository, 'locate', '-r', version]).splitlines() | 
| -  genericFilename = 'metadata' | 
| -  filename = '%s.%s' % (genericFilename, repo.type) | 
| - | 
| -  # Fall back to platform-independent metadata file | 
| -  if filename not in files: | 
| -    filename = genericFilename | 
| - | 
| -  command = ['hg', '-R', repo.repository, 'cat', '-r', version, os.path.join(repo.repository, filename)] | 
| -  result = subprocess.check_output(command) | 
| - | 
| -  parser = SafeConfigParser() | 
| -  parser.readfp(StringIO(result)) | 
| - | 
| -  return parser | 
| - | 
| def readMetadata(repo, version): | 
| """ | 
| reads extension ID and compatibility information from metadata file in the | 
| @@ -202,17 +181,12 @@ | 
| result = subprocess.check_output(command) | 
| revision = re.sub(r'\D', '', result) | 
|  | 
| -    command = ['hg', '-R', repo.repository, 'cat', '-r', version, os.path.join(repo.repository, 'AndroidManifest.xml')] | 
| -    result = subprocess.check_output(command) | 
| -    manifest = dom.parseString(result) | 
| -    usesSdk = manifest.getElementsByTagName('uses-sdk')[0] | 
| - | 
| return { | 
| 'revision': revision, | 
| -      'minSdkVersion': usesSdk.attributes["android:minSdkVersion"].value, | 
| +      'minSdkVersion': get_min_sdk_version(repo, version), | 
| } | 
| elif repo.type == 'safari': | 
| -    metadata = readRawMetadata(repo, version) | 
| +    metadata = repo.readMetadata(version) | 
| return { | 
| 'certificateID': getSafariCertificateID(repo.keyFile), | 
| 'version': version, | 
| @@ -220,7 +194,7 @@ | 
| 'basename': metadata.get('general', 'basename'), | 
| } | 
| elif repo.type == 'gecko': | 
| -    metadata = readRawMetadata(repo, version) | 
| +    metadata = repo.readMetadata(version) | 
| result = { | 
| 'extensionID': metadata.get('general', 'id'), | 
| 'version': version, | 
| @@ -257,6 +231,12 @@ | 
| template = get_template(get_config().get('extensions', '%sUpdateManifest' % repoType)) | 
| template.stream({'extensions': extensions[repoType]}).dump(manifestPath) | 
|  | 
| +def writePadFile(links): | 
| +  for repo in Configuration.getRepositoryConfigurations(): | 
| +    if repo.padTemplate and links.has_section(repo.repositoryName): | 
| +      PadFile.forRepository(repo, links.get(repo.repositoryName, 'version'), | 
| +                                  links.get(repo.repositoryName, 'downloadURL')).write() | 
| + | 
| def updateLinks(): | 
| """ | 
| writes the current extension download links to a file | 
| @@ -270,6 +250,7 @@ | 
| file.close() | 
|  | 
| writeUpdateManifest(result) | 
| +  writePadFile(result) | 
|  | 
| if __name__ == "__main__": | 
| updateLinks() | 
|  |