+
+ /**
+ * Fetch a list of other themes whose CSS needs to be pulled in before
+ * this theme's, based on following the theme.ini 'include' settings.
+ * (May be empty if this theme has no include dependencies.)
+ *
+ * @return array of strings with theme names
+ */
+ function getDeps()
+ {
+ $chain = $this->doGetDeps(array($this->name));
+ array_pop($chain); // Drop us back off
+ return $chain;
+ }
+
+ protected function doGetDeps($chain)
+ {
+ $data = $this->getMetadata();
+ if (!empty($data['include'])) {
+ $include = $data['include'];
+
+ // Protect against cycles!
+ if (!in_array($include, $chain)) {
+ try {
+ $theme = new Theme($include);
+ array_unshift($chain, $include);
+ return $theme->doGetDeps($chain);
+ } catch (Exception $e) {
+ common_log(LOG_ERR,
+ "Exception while fetching theme dependencies " .
+ "for $this->name: " . $e->getMessage());
+ }
+ }
+ }
+ return $chain;
+ }
+
+ /**
+ * Pull data from the theme's theme.ini file.
+ * @fixme calling getFile will fall back to default theme, this may be unsafe.
+ *
+ * @return associative array of strings
+ */
+ function getMetadata()
+ {
+ $iniFile = $this->getFile('theme.ini');
+ if (file_exists($iniFile)) {
+ return parse_ini_file($iniFile);
+ } else {
+ return array();
+ }
+ }
+
+ /**
+ * Gets the full path of a file in a theme dir based on its relative name
+ *
+ * @param string $relative relative path within the theme directory
+ * @param string $name name of the theme; defaults to current theme
+ *
+ * @return string File path to the theme file
+ */
+ static function file($relative, $name=null)
+ {
+ $theme = new Theme($name);
+ return $theme->getFile($relative);
+ }
+
+ /**
+ * Gets the full URL of a file in a theme dir based on its relative name
+ *
+ * @param string $relative relative path within the theme directory
+ * @param string $name name of the theme; defaults to current theme
+ *
+ * @return string URL of the file
+ */
+ static function path($relative, $name=null)
+ {
+ $theme = new Theme($name);
+ return $theme->getPath($relative);
+ }
+
+ /**
+ * list available theme names
+ *
+ * @return array list of available theme names
+ */
+ static function listAvailable()
+ {
+ $local = self::subdirsOf(self::localRoot());
+ $install = self::subdirsOf(self::installRoot());
+
+ $i = array_search('base', $install);
+
+ unset($install[$i]);
+
+ return array_merge($local, $install);
+ }
+
+ /**
+ * Utility for getting subdirs of a directory
+ *
+ * @param string $dir full path to directory to check
+ *
+ * @return array relative filenames of subdirs, or empty array
+ */
+ protected static function subdirsOf($dir)
+ {
+ $subdirs = array();
+
+ if (is_dir($dir)) {
+ if ($dh = opendir($dir)) {
+ while (($filename = readdir($dh)) !== false) {
+ if ($filename != '..' && $filename !== '.' &&
+ is_dir($dir.'/'.$filename)) {
+ $subdirs[] = $filename;
+ }
+ }
+ closedir($dh);
+ }
+ }
+
+ return $subdirs;
+ }
+
+ /**
+ * Local root dir for themes
+ *
+ * @return string local root dir for themes
+ */
+ protected static function localRoot()
+ {
+ $basedir = common_config('local', 'dir');
+
+ if (empty($basedir)) {
+ $basedir = INSTALLDIR . '/local';
+ }
+
+ return $basedir . '/theme';
+ }
+
+ /**
+ * Root dir for themes that are shipped with StatusNet
+ *
+ * @return string root dir for StatusNet themes
+ */
+ protected static function installRoot()
+ {
+ $instroot = common_config('theme', 'dir');
+
+ if (empty($instroot)) {
+ $instroot = INSTALLDIR.'/theme';
+ }
+
+ return $instroot;
+ }
+
+ static function validName($name)
+ {
+ return preg_match('/^[a-z0-9][a-z0-9_-]*$/i', $name);