displayXmlError($error);
}
@libxml_clear_errors();
throw new SpecParserException("Invalid XML Document
\n" . $xmlErrors);
}
if (count($doc->ModulePrefs) != 1) {
throw new SpecParserException("Missing or duplicated ");
}
$gadget = new Gadget($context->getGadgetId(), $context);
// record Checksum to trace xml version
$gadget->setChecksum($xml);
// process ModulePref attributes
$this->processModulePrefs($gadget, $doc->ModulePrefs);
if (isset($doc->ModulePrefs->OAuth)) {
// process OAuthPref attributes
$this->processOAuthSpec($gadget, $doc->ModulePrefs->OAuth, $context);
}
// process UserPrefs, if any
foreach ($doc->UserPref as $pref) {
$this->processUserPref($gadget, $pref);
}
// Assume gadget v1
$explicit_profile = false;
foreach ($doc->Content as $content) {
$attributes = $content->attributes();
if (empty($attributes['type'])) {
throw new SpecParserException("No content type specified!");
}
$view = isset($attributes['view']) ? trim($attributes['view']) : '';
// Note if we find a profile explicity defined
if (strpos($view, "profile") !== false) {
$explicit_profile = true;
}
}
foreach ($doc->Content as $content) {
$attributes = $content->attributes();
if (empty($attributes['type'])) {
throw new SpecParserException("No content type specified!");
}
$view = isset($attributes['view']) ? trim($attributes['view']) : '';
// If view isnt defined and we didnt find a profile explicity defined
if ($view == '') {
if (! $explicit_profile) {
$this->processContent($gadget, $content, array(0 => DEFAULT_VIEW));
}
// If view isnt defined and a profile was found, this will catch it
} else {
$views = explode(',', $view);
$this->processContent($gadget, $content, $views);
}
}
foreach ($doc->ModulePrefs->Preload as $feature) {
$gadget->preloads[] = new Preload($feature);
}
foreach ($doc->ModulePrefs->Require as $feature) {
$this->processFeature($gadget, $feature, true);
}
foreach ($doc->ModulePrefs->Optional as $feature) {
$this->processFeature($gadget, $feature, false);
}
foreach ($doc->ModulePrefs->Icon as $icon) {
$this->processIcon($gadget, $icon);
}
return $gadget;
}
private function processModulePrefs(&$gadget, $ModulePrefs) {
$attributes = $ModulePrefs->attributes();
if (empty($attributes['title'])) {
throw new SpecParserException("Missing or empty \"title\" attribute.");
}
// Get ModulePrefs base and extended attributes
// See http://code.google.com/apis/gadgets/docs/gadgets-extended-xsd.html
// (trim is used here since it not only cleans up the text, but also auto-casts the SimpleXMLElement to a string)
$gadget->title = trim($attributes['title']);
$gadget->author = isset($attributes['author']) ? trim($attributes['author']) : '';
$gadget->authorEmail = isset($attributes['author_email']) ? trim($attributes['author_email']) : '';
$gadget->description = isset($attributes['description']) ? trim($attributes['description']) : '';
$gadget->directoryTitle = isset($attributes['directory_title']) ? trim($attributes['directory_title']) : '';
$gadget->screenshot = isset($attributes['screenshot']) ? trim($attributes['screenshot']) : '';
$gadget->thumbnail = isset($attributes['thumbnail']) ? trim($attributes['thumbnail']) : '';
$gadget->titleUrl = isset($attributes['title_url']) ? trim($attributes['title_url']) : '';
// Extended spec fields
$gadget->authorAffiliation = isset($attributes['author_affiliation']) ? trim($attributes['author_affiliation']) : '';
$gadget->authorLocation = isset($attributes['author_location']) ? trim($attributes['author_location']) : '';
$gadget->authorPhoto = isset($attributes['author_photo']) ? trim($attributes['author_photo']) : '';
$gadget->authorAboutMe = isset($attributes['author_aboutme']) ? trim($attributes['author_aboutme']) : '';
$gadget->authorQuote = isset($attributes['author_quote']) ? trim($attributes['author_quote']) : '';
$gadget->authorLink = isset($attributes['author_link']) ? trim($attributes['author_link']) : '';
$gadget->showStats = isset($attributes['show_stats']) ? trim($attributes['show_stats']) : '';
$gadget->showInDirectory = isset($attributes['show_in_directory']) ? trim($attributes['show_in_directory']) : '';
$gadget->string = isset($attributes['string']) ? trim($attributes['string']) : '';
$gadget->width = isset($attributes['width']) ? trim($attributes['width']) : '';
$gadget->height = isset($attributes['height']) ? trim($attributes['height']) : '';
$gadget->category = isset($attributes['category']) ? trim($attributes['category']) : '';
$gadget->category2 = isset($attributes['category2']) ? trim($attributes['category2']) : '';
$gadget->singleton = isset($attributes['singleton']) ? trim($attributes['singleton']) : '';
$gadget->renderInline = isset($attributes['render_inline']) ? trim($attributes['render_inline']) : '';
$gadget->scaling = isset($attributes['scaling']) ? trim($attributes['scaling']) : '';
$gadget->scrolling = isset($attributes['scrolling']) ? trim($attributes['scrolling']) : '';
foreach ($ModulePrefs->Link as $link) {
$gadget->links[] = $this->processLink($link);
}
foreach ($ModulePrefs->Locale as $locale) {
$gadget->localeSpecs[] = $this->processLocale($locale);
}
}
private function processLink($link) {
$attributes = $link->attributes();
$rel = isset($attributes['rel']) ? trim($attributes['rel']) : '';
$href = isset($attributes['href']) ? trim($attributes['href']) : '';
$method = isset($attributes['method']) ? trim($attributes['method']) : 'GET';
$link = new LinkSpec($rel, $href, $method);
return $link;
}
private function processLocale($locale) {
$attributes = $locale->attributes();
$messageAttr = isset($attributes['messages']) ? trim($attributes['messages']) : '';
$languageAttr = isset($attributes['lang']) ? trim($attributes['lang']) : 'all';
$countryAttr = isset($attributes['country']) ? trim($attributes['country']) : 'all';
$rtlAttr = isset($attributes['language_direction']) ? trim($attributes['language_direction']) : '';
$rightToLeft = $rtlAttr == 'rtl';
$localeMessageBundles = array();
if ($messageAttr == '') {
$parser = new MessageBundleParser();
$localeMessageBundles = $parser->getMessages($locale);
}
$locale = new LocaleSpec();
$locale->rightToLeft = $rightToLeft;
$locale->url = $messageAttr;
$locale->localeMessageBundles = $localeMessageBundles;
$locale->locale = new Locale($languageAttr, $countryAttr);
return $locale;
}
private function processUserPref(&$gadget, $pref) {
$attributes = $pref->attributes();
$preference = new UserPref();
if (empty($attributes['name'])) {
throw new SpecParserException("All UserPrefs must have name attributes.");
}
$preference->name = trim($attributes['name']);
$preference->displayName = isset($attributes['display_name']) ? $gadget->getSubstitutions()->substitute(trim($attributes['display_name'])) : '';
// if its set -and- in our valid 'enum' of types, use it, otherwise assume STRING, to try and emulate java's enum behavior
$preference->required = isset($attributes['required']) ? $gadget->getSubstitutions()->substitute(trim($attributes['required'])) : 'false';
$preference->dataType = isset($attributes['datatype']) && in_array(strtoupper($attributes['datatype']), $preference->DataTypes) ? strtoupper($attributes['datatype']) : 'STRING';
$preference->defaultValue = isset($attributes['default_value']) ? $gadget->getSubstitutions()->substitute(trim($attributes['default_value'])) : '';
if (isset($pref->EnumValue)) {
foreach ($pref->EnumValue as $enum) {
$attr = $enum->attributes();
$valueText = trim($attr['value']);
$displayText = ! empty($attr['display_value']) ? $gadget->getSubstitutions()->substitute(trim($attr['display_value'])) : $valueText;
$preference->enumValues[$valueText] = $displayText;
}
}
$gadget->userPrefs[] = $preference;
}
private function processContent(&$gadget, $content, $views) {
$html = (string)$content; // no trim here since empty lines can have structural meaning, so typecast to string instead
foreach ($views as $view) {
$viewSpec = new ViewSpec($view, $content);
if (! isset($gadget->views[$view])) {
$viewSpec->content = $html;
$gadget->views[$view] = $viewSpec;
} else {
if ($gadget->views[$view]->getName() == $viewSpec->getName() && $viewSpec->getType() != $gadget->views[$view]->getType()) {
throw new SpecParserException("You may not mix content " . " types in the same view.");
}
$gadget->views[$view]->addContent($html);
}
}
}
private function processFeature(&$gadget, $feature, $required) {
$featureSpec = new FeatureSpec();
$attributes = $feature->attributes();
if (empty($attributes['feature'])) {
throw new SpecParserException("Feature not specified in <" . ($required ? "Required" : "Optional") . "> tag");
}
$featureSpec->name = trim($attributes['feature']);
$featureSpec->optional = ! $required;
foreach ($feature->Param as $param) {
$attr = $param->attributes();
if (empty($attr['name'])) {
throw new SpecParserException("Missing name attribute in .");
}
$name = trim($attr['name']);
$value = trim($param);
$featureSpec->params[$name] = $value;
}
$gadget->requires[$featureSpec->name] = $featureSpec;
}
private function processIcon(Gadget &$gadget, $icon) {
$attributes = $icon->attributes();
$iconSpec = new Icon();
$iconSpec->content = (string)(trim($icon));
$iconSpec->mode = isset($attributes['mode']) ? trim($attributes['mode']) : '';
$iconSpec->type = isset($attributes['type']) ? trim($attributes['type']) : '';
$gadget->icons[] = $iconSpec;
}
private function processOAuthSpec(Gadget &$gadget, $OAuthSpec) {
$oauthSpec = new OAuthSpec($OAuthSpec->Service);
$gadget->setOAuthSpec($oauthSpec);
}
private function displayXmlError($error) {
$return = '';
switch ($error->level) {
case LIBXML_ERR_WARNING:
$return .= "Warning [{$error->code}]: ";
break;
case LIBXML_ERR_ERROR:
$return .= "Error [{$error->code}]: ";
break;
case LIBXML_ERR_FATAL:
$return .= "Fatal Error [{$error->code}]: ";
break;
}
$return .= trim($error->message) . ", Line: {$error->line}" . " Column: {$error->column}";
if ($error->file) {
$return .= "File: {$error->file}";
}
return "$return\n";
}
}