'notice' => array('contentlimit'),
'throttle' => array('enabled', 'count', 'timespan'),
'xmpp' => array('enabled', 'server', 'port', 'user'),
- 'integration' => array('source')
+ 'integration' => array('source'),
+ 'attachments' => array('uploads', 'file_quota')
);
/**
foreach ($this->keys as $section => $settings) {
$this->elementStart($section);
foreach ($settings as $setting) {
- $value = common_config($section, $setting);
+ $value = $this->setting($section, $setting);
if (is_array($value)) {
$value = implode(',', $value);
} else if ($value === false || $value == '0') {
$result[$section] = array();
foreach ($settings as $setting) {
$result[$section][$setting]
- = common_config($section, $setting);
+ = $this->setting($section, $setting);
}
}
$this->initDocument('json');
}
}
+ function setting($section, $key) {
+ $result = common_config($section, $key);
+ if ($key == 'file_quota') {
+ // hack: adjust for the live upload limit
+ if (common_config($section, 'uploads')) {
+ $max = ImageFile::maxFileSizeInt();
+ } else {
+ $max = 0;
+ }
+ return min($result, $max);
+ }
+ return $result;
+ }
+
/**
* Return true if read only.
*
$options = implode('|', array_map('preg_quote', array_map('htmlspecialchars', $terms),
array_fill(0, sizeof($terms), '/')));
$pattern = "/($options)/i";
- $result = preg_replace($pattern, '<strong>\\1</strong>', $text);
+ $result = '';
+
+ /* Divide up into text (highlight me) and tags (don't touch) */
+ $chunks = preg_split('/(<[^>]+>)/', $text, 0, PREG_SPLIT_DELIM_CAPTURE);
+ foreach ($chunks as $i => $chunk) {
+ if ($i % 2 == 1) {
+ // odd: delimiter (tag)
+ $result .= $chunk;
+ } else {
+ // even: freetext between tags
+ $result .= preg_replace($pattern, '<strong>\\1</strong>', $chunk);
+ }
+ }
- /* Remove highlighting from inside links, loop incase multiple highlights in links */
- $pattern = '/(\w+="[^"]*)<strong>('.$options.')<\/strong>([^"]*")/iU';
- do {
- $result = preg_replace($pattern, '\\1\\2\\3', $result, -1, $count);
- } while ($count);
return $result;
}
}
function showFooter()
{
$this->elementStart('div', array('id' => 'footer'));
- $this->showSecondaryNav();
- $this->showLicenses();
+ if (Event::handle('StartShowInsideFooter', array($this))) {
+ $this->showSecondaryNav();
+ $this->showLicenses();
+ Event::handle('EndShowInsideFooter', array($this));
+ }
$this->elementEnd('div');
}
$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'])) {
$this->serveMobile = true;
+ } else if (isset($_COOKIE['MobileOverride'])) {
+ // Cookie override is controlled by link at bottom.
+ $this->serveMobile = (bool)$_COOKIE['MobileOverride'];
} else {
// If they like the WAP 2.0 mimetype, serve them MP
// @fixme $type is undefined, making this if case useless and spewing errors.
}
}
- function onStartShowScripts($action)
+ function onEndShowScripts($action)
{
+ $action->inlineScript('
+ $(function() {
+ $("#mobile-toggle-disable").click(function() {
+ $.cookie("MobileOverride", "0", {path: "/"});
+ window.location.reload();
+ return false;
+ });
+ $("#mobile-toggle-enable").click(function() {
+ $.cookie("MobileOverride", "1", {path: "/"});
+ window.location.reload();
+ return false;
+ });
+ });'
+ );
+ }
+
+ function onEndShowInsideFooter($action)
+ {
+ if ($this->serveMobile) {
+ // TRANS: Link to switch site layout from mobile to desktop mode. Appears at very bottom of page.
+ $linkText = _m('Switch to desktop site layout.');
+ $key = 'mobile-toggle-disable';
+ } else {
+ // TRANS: Link to switch site layout from desktop to mobile mode. Appears at very bottom of page.
+ $linkText = _m('Switch to mobile site layout.');
+ $key = 'mobile-toggle-enable';
+ }
+ $action->elementStart('p');
+ $action->element('a', array('href' => '#', 'id' => $key), $linkText);
+ $action->elementEnd('p');
+ return true;
}
function _common_path($relative, $ssl=false)
}
// Move all the entities into order so we can
- // replace them in reverse order and thus
- // not mess up their indices
+ // replace them and escape surrounding plaintext
+ // in order
$toReplace = array();
}
}
- // sort in reverse order by key
+ // sort in forward order by key
- krsort($toReplace);
+ ksort($toReplace);
+
+ $result = '';
+ $cursor = 0;
foreach ($toReplace as $part) {
list($type, $object) = $part;
+ $start = $object->indices[0];
+ $end = $object->indices[1];
+ if ($cursor < $start) {
+ // Copy in the preceding plaintext
+ $result .= $this->twitEscape(mb_substr($text, $cursor, $start - $cursor));
+ $cursor = $start;
+ }
+ $orig = $this->twitEscape(mb_substr($text, $start, $end - $start));
switch($type) {
case self::URL:
- $linkText = $this->makeUrlLink($object);
+ $linkText = $this->makeUrlLink($object, $orig);
break;
case self::HASHTAG:
- $linkText = $this->makeHashtagLink($object);
+ $linkText = $this->makeHashtagLink($object, $orig);
break;
case self::MENTION:
- $linkText = $this->makeMentionLink($object);
+ $linkText = $this->makeMentionLink($object, $orig);
break;
default:
+ $linkText = $orig;
continue;
}
- $text = mb_substr($text, 0, $object->indices[0]) . $linkText . mb_substr($text, $object->indices[1]);
+ $result .= $linkText;
+ $cursor = $end;
}
- return $text;
+ $last = $this->twitEscape(mb_substr($text, $cursor));
+ $result .= $last;
+
+ return $result;
+ }
+
+ function twitEscape($str)
+ {
+ // Twitter seems to preemptive turn < and > into < and >
+ // but doesn't for &, so while you may have some magic protection
+ // against XSS by not bothing to escape manually, you still get
+ // invalid XHTML. Thanks!
+ //
+ // Looks like their web interface pretty much sends anything
+ // through intact, so.... to do equivalent, decode all entities
+ // and then re-encode the special ones.
+ return htmlspecialchars(html_entity_decode($str, ENT_COMPAT, 'UTF-8'));
}
- function makeUrlLink($object)
+ function makeUrlLink($object, $orig)
{
- return "<a href='{$object->url}' class='extlink'>{$object->url}</a>";
+ return "<a href='{$object->url}' class='extlink'>{$orig}</a>";
}
- function makeHashtagLink($object)
+ function makeHashtagLink($object, $orig)
{
- return "#" . self::tagLink($object->text);
+ return "#" . self::tagLink($object->text, substr($orig, 1));
}
- function makeMentionLink($object)
+ function makeMentionLink($object, $orig)
{
- return "@".self::atLink($object->screen_name, $object->name);
+ return "@".self::atLink($object->screen_name, $object->name, substr($orig, 1));
}
- static function tagLink($tag)
+ static function tagLink($tag, $orig)
{
- return "<a href='https://search.twitter.com/search?q=%23{$tag}' class='hashtag'>{$tag}</a>";
+ return "<a href='https://search.twitter.com/search?q=%23{$tag}' class='hashtag'>{$orig}</a>";
}
- static function atLink($screenName, $fullName=null)
+ static function atLink($screenName, $fullName, $orig)
{
if (!empty($fullName)) {
- return "<a href='http://twitter.com/#!/{$screenName}' title='{$fullName}'>{$screenName}</a>";
+ return "<a href='http://twitter.com/#!/{$screenName}' title='{$fullName}'>{$orig}</a>";
} else {
- return "<a href='http://twitter.com/#!/{$screenName}'>{$screenName}</a>";
+ return "<a href='http://twitter.com/#!/{$screenName}'>{$orig}</a>";
}
}