]> git.mxchange.org Git - quix0rs-gnu-social.git/commitdiff
Merge branch '0.9.x' of git@gitorious.org:statusnet/mainline into 0.9.x
authorEvan Prodromou <evan@status.net>
Mon, 28 Dec 2009 23:50:41 +0000 (15:50 -0800)
committerEvan Prodromou <evan@status.net>
Mon, 28 Dec 2009 23:50:41 +0000 (15:50 -0800)
Conflicts:
scripts/setconfig.php

23 files changed:
EVENTS.txt
actions/apiblockcreate.php
actions/apiblockdestroy.php
actions/apistatusesupdate.php
actions/block.php
actions/newnotice.php
actions/profilesettings.php
actions/unblock.php
classes/Notice.php
classes/User.php
classes/User_location_prefs.php [new file with mode: 0644]
classes/statusnet.ini
db/08to09.sql
db/statusnet.sql
lib/default.php
plugins/Blacklist/BlacklistPlugin.php [new file with mode: 0644]
plugins/UserFlag/UserFlagPlugin.php
plugins/UserFlag/User_flag_profile.php
plugins/UserFlag/adminprofileflag.php
plugins/UserFlag/clearflag.php [new file with mode: 0644]
plugins/UserFlag/clearflagform.php [new file with mode: 0644]
plugins/UserFlag/flagprofile.php
scripts/setconfig.php [changed mode: 0755->0644]

index 96250f64c784dc665d78d1a0fdadf8653c9fe556..64e345b6926b1800f12933d17453805a2de7dfcc 100644 (file)
@@ -640,3 +640,18 @@ EndLog: After writing to the logs
 - $msg
 - $filename
 
+StartBlockProfile: when we're about to block
+- $user: the person doing the block
+- $profile: the person getting blocked, can be remote
+
+EndBlockProfile: when a block has succeeded
+- $user: the person doing the block
+- $profile: the person blocked, can be remote
+
+StartUnblockProfile: when we're about to unblock
+- $user: the person doing the unblock
+- $profile: the person getting unblocked, can be remote
+
+EndUnblockProfile: when an unblock has succeeded
+- $user: the person doing the unblock
+- $profile: the person unblocked, can be remote
index e79dec32d0521ced06d3da89c6a4226ac05e89d0..c26485f591b23196917eb07f3fcc8b65e6ab72d3 100644 (file)
@@ -109,9 +109,16 @@ class ApiBlockCreateAction extends ApiAuthAction
             return;
         }
 
-        if ($this->user->hasBlocked($this->other)
-            || $this->user->block($this->other)
-        ) {
+        if (!$this->user->hasBlocked($this->other)) {
+            if (Event::handle('StartBlockProfile', array($this->user, $this->other))) {
+                $result = $this->user->block($this->other);
+                if ($result) {
+                    Event::handle('EndBlockProfile', array($this->user, $this->other));
+                }
+            }
+        }
+
+        if ($this->user->hasBlocked($this->other)) {
             $this->initDocument($this->format);
             $this->showProfile($this->other, $this->format);
             $this->endDocument($this->format);
index 328f18ab0dcf1765d688f807160b314ac4ba3dd2..666f308f4c5a3819ce1c83ff35e9fb9620c4b5d2 100644 (file)
@@ -97,9 +97,16 @@ class ApiBlockDestroyAction extends ApiAuthAction
             return;
         }
 
-        if (!$this->user->hasBlocked($this->other)
-            || $this->user->unblock($this->other)
-        ) {
+        if ($this->user->hasBlocked($this->other)) {
+            if (Event::handle('StartUnblockProfile', array($this->user, $this->other))) {
+                $result = $this->user->unblock($this->other);
+                if ($result) {
+                    Event::handle('EndUnblockProfile', array($this->user, $this->other));
+                }
+            }
+        }
+
+        if (!$this->user->hasBlocked($this->other)) {
             $this->initDocument($this->format);
             $this->showProfile($this->other, $this->format);
             $this->endDocument($this->format);
index dabbea92f5246c747b913a16bd70a8c3308c7a9d..f594bbf393626ffa67cccc8d5806ea9629df864a 100644 (file)
@@ -203,12 +203,6 @@ class ApiStatusesUpdateAction extends ApiAuthAction
                 }
             }
 
-            $location = null;
-
-            if (!empty($this->lat) && !empty($this->lon)) {
-                $location = Location::fromLatLon($this->lat, $this->lon);
-            }
-
             $upload = null;
 
             try {
@@ -235,11 +229,15 @@ class ApiStatusesUpdateAction extends ApiAuthAction
 
             $options = array('reply_to' => $reply_to);
 
-            if (!empty($location)) {
-                $options['lat'] = $location->lat;
-                $options['lon'] = $location->lon;
-                $options['location_id'] = $location->location_id;
-                $options['location_ns'] = $location->location_ns;
+            if ($this->user->shareLocation()) {
+
+                $locOptions = Notice::locationOptions($this->lat,
+                                                      $this->lon,
+                                                      null,
+                                                      null,
+                                                      $this->user->getProfile());
+
+                $options = array_merge($options, $locOptions);
             }
 
             $this->notice =
index 71a34e08793d1bdd0796748fd9c3a4bb5a3528b3..5fae45dffcd46b0140d00e1cb79488abc707a9c1 100644 (file)
@@ -156,7 +156,12 @@ class BlockAction extends ProfileFormAction
     {
         $cur = common_current_user();
 
-        $result = $cur->block($this->profile);
+        if (Event::handle('StartBlockProfile', array($cur, $this->profile))) {
+            $result = $cur->block($this->profile);
+            if ($result) {
+                Event::handle('EndBlockProfile', array($cur, $this->profile));
+            }
+        }
 
         if (!$result) {
             $this->serverError(_('Failed to save block information.'));
@@ -164,4 +169,3 @@ class BlockAction extends ProfileFormAction
         }
     }
 }
-
index c014f1781cd9b380b46a160e3113bc846057327f..2d9f0ff797d973fc31e5970a28061859fc7054d7 100644 (file)
@@ -164,19 +164,6 @@ class NewnoticeAction extends Action
             $replyto = 'false';
         }
 
-        $lat = $this->trimmed('lat');
-        $lon = $this->trimmed('lon');
-        $location_id = $this->trimmed('location_id');
-        $location_ns = $this->trimmed('location_ns');
-
-        if (!empty($lat) && !empty($lon) && empty($location_id)) {
-            $location = Location::fromLatLon($lat, $lon);
-            if (!empty($location)) {
-                $location_id = $location->location_id;
-                $location_ns = $location->location_ns;
-            }
-        }
-
         $upload = null;
         $upload = MediaFile::fromUpload('attach');
 
@@ -195,12 +182,20 @@ class NewnoticeAction extends Action
             }
         }
 
-        $notice = Notice::saveNew($user->id, $content_shortened, 'web',
-                                  array('reply_to' => ($replyto == 'false') ? null : $replyto,
-                                        'lat' => $lat,
-                                        'lon' => $lon,
-                                        'location_id' => $location_id,
-                                        'location_ns' => $location_ns));
+        $options = array('reply_to' => ($replyto == 'false') ? null : $replyto);
+
+        if ($user->shareLocation()) {
+
+            $locOptions = Notice::locationOptions($this->trimmed('lat'),
+                                                  $this->trimmed('lon'),
+                                                  $this->trimmed('location_id'),
+                                                  $this->trimmed('location_ns'),
+                                                  $user->getProfile());
+
+            $options = array_merge($options, $locOptions);
+        }
+
+        $notice = Notice::saveNew($user->id, $content_shortened, 'web', $options);
 
         if (isset($upload)) {
             $upload->attachToNotice($notice);
index acfcbcd00747e38832124cc9e78139786f1700bd..ee236fe62503ec3b3cd7adf5af58e813a3605713 100644 (file)
@@ -133,6 +133,13 @@ class ProfilesettingsAction extends AccountSettingsAction
                          ($this->arg('location')) ? $this->arg('location') : $profile->location,
                          _('Where you are, like "City, State (or Region), Country"'));
             $this->elementEnd('li');
+            if (common_config('location', 'share') == 'user') {
+                $this->elementStart('li');
+                $this->checkbox('sharelocation', _('Share my current location when posting notices'),
+                                ($this->arg('sharelocation')) ?
+                                $this->arg('sharelocation') : $user->shareLocation());
+                $this->elementEnd('li');
+            }
             Event::handle('EndProfileFormData', array($this));
             $this->elementStart('li');
             $this->input('tags', _('Tags'),
@@ -318,6 +325,37 @@ class ProfilesettingsAction extends AccountSettingsAction
 
             $profile->profileurl = common_profile_url($nickname);
 
+            if (common_config('location', 'share') == 'user') {
+
+                $exists = false;
+
+                $prefs = User_location_prefs::staticGet('user_id', $user->id);
+
+                if (empty($prefs)) {
+                    $prefs = new User_location_prefs();
+
+                    $prefs->user_id = $user->id;
+                    $prefs->created = common_sql_now();
+                } else {
+                    $exists = true;
+                    $orig = clone($prefs);
+                }
+
+                $prefs->share_location = $this->boolean('sharelocation');
+
+                if ($exists) {
+                    $result = $prefs->update($orig);
+                } else {
+                    $result = $prefs->insert();
+                }
+
+                if ($result === false) {
+                    common_log_db_error($prefs, ($exists) ? 'UPDATE' : 'INSERT', __FILE__);
+                    $this->serverError(_('Couldn\'t save location prefs.'));
+                    return;
+                }
+            }
+
             common_debug('Old profile: ' . common_log_objstring($orig_profile), __FILE__);
             common_debug('New profile: ' . common_log_objstring($profile), __FILE__);
 
index c60458cd3ca810486714a1ea184e30d06cd51f45..0f63e1dae05802c4308f52e7f644d63fb81cbec1 100644 (file)
@@ -71,8 +71,17 @@ class UnblockAction extends ProfileFormAction
 
     function handlePost()
     {
-        $cur    = common_current_user();
-        $result = $cur->unblock($this->profile);
+        $cur = common_current_user();
+
+        $result = false;
+
+        if (Event::handle('StartUnblockProfile', array($cur, $this->profile))) {
+            $result = $cur->unblock($this->profile);
+            if ($result) {
+                Event::handle('EndUnblockProfile', array($cur, $this->profile));
+            }
+        }
+
         if (!$result) {
             $this->serverError(_('Error removing the block.'));
             return;
index 7651d8bd50e4bd4fbc80022ea170d90551388cb9..9f68c52553d0647ae382685f2ec0ba50c6924ba0 100644 (file)
@@ -289,21 +289,11 @@ class Notice extends Memcached_DataObject
         if (!empty($lat) && !empty($lon)) {
             $notice->lat = $lat;
             $notice->lon = $lon;
+        }
+
+        if (!empty($location_ns) && !empty($location_id)) {
             $notice->location_id = $location_id;
             $notice->location_ns = $location_ns;
-        } else if (!empty($location_ns) && !empty($location_id)) {
-            $location = Location::fromId($location_id, $location_ns);
-            if (!empty($location)) {
-                $notice->lat = $location->lat;
-                $notice->lon = $location->lon;
-                $notice->location_id = $location_id;
-                $notice->location_ns = $location_ns;
-            }
-        } else {
-            $notice->lat         = $profile->lat;
-            $notice->lon         = $profile->lon;
-            $notice->location_id = $profile->location_id;
-            $notice->location_ns = $profile->location_ns;
         }
 
         if (Event::handle('StartNoticeSave', array(&$notice))) {
@@ -1429,4 +1419,47 @@ class Notice extends Memcached_DataObject
 
         return $ids;
     }
+
+    function locationOptions($lat, $lon, $location_id, $location_ns, $profile = null)
+    {
+        $options = array();
+
+        if (!empty($location_id) && !empty($location_ns)) {
+
+            $options['location_id'] = $location_id;
+            $options['location_ns'] = $location_ns;
+
+            $location = Location::fromId($location_id, $location_ns);
+
+            if (!empty($location)) {
+                $options['lat'] = $location->lat;
+                $options['lon'] = $location->lon;
+            }
+
+        } else if (!empty($lat) && !empty($lon)) {
+
+            $options['lat'] = $lat;
+            $options['lon'] = $lon;
+
+            $location = Location::fromLatLon($lat, $lon);
+
+            if (!empty($location)) {
+                $options['location_id'] = $location->location_id;
+                $options['location_ns'] = $location->location_ns;
+            }
+        } else if (!empty($profile)) {
+
+            if (isset($profile->lat) && isset($profile->lon)) {
+                $options['lat'] = $profile->lat;
+                $options['lon'] = $profile->lon;
+            }
+
+            if (isset($profile->location_id) && isset($profile->location_ns)) {
+                $options['location_id'] = $profile->location_id;
+                $options['location_ns'] = $profile->location_ns;
+            }
+        }
+
+        return $options;
+    }
 }
index 484dc8c82b30a09cd19ebf92366d4a460898a60a..34151778c5c3274b89cf6f6562334e11c29249a7 100644 (file)
@@ -625,7 +625,11 @@ class User extends Memcached_DataObject
 
         // Cancel their subscription, if it exists
 
-        subs_unsubscribe_to($other->getUser(),$this->getProfile());
+        $otherUser = User::staticGet('id', $other->id);
+
+        if (!empty($otherUser)) {
+            subs_unsubscribe_to($otherUser, $this->getProfile());
+        }
 
         $block->query('COMMIT');
 
@@ -992,4 +996,28 @@ class User extends Memcached_DataObject
 
         return $ids;
     }
+
+    function shareLocation()
+    {
+        $cfg = common_config('location', 'share');
+
+        if ($cfg == 'always') {
+            return true;
+        } else if ($cfg == 'never') {
+            return false;
+        } else { // user
+            $share = true;
+
+            $prefs = User_location_prefs::staticGet('user_id', $this->id);
+
+            if (empty($prefs)) {
+                $share = common_config('location', 'sharedefault');
+            } else {
+                $share = $prefs->share_location;
+                $prefs->free();
+            }
+
+            return $share;
+        }
+    }
 }
diff --git a/classes/User_location_prefs.php b/classes/User_location_prefs.php
new file mode 100644 (file)
index 0000000..52cb254
--- /dev/null
@@ -0,0 +1,48 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Data class for user location preferences
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Data
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2009 StatusNet Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
+
+class User_location_prefs extends Memcached_DataObject
+{
+    ###START_AUTOCODE
+    /* the code below is auto generated do not remove the above tag */
+
+    public $__table = 'user_location_prefs';             // table name
+    public $user_id;                         // int(4)  primary_key not_null
+    public $share_location;                  // tinyint(1)   default_1
+    public $created;                         // datetime   not_null default_0000-00-00%2000%3A00%3A00
+    public $modified;                        // timestamp   not_null default_CURRENT_TIMESTAMP
+
+    /* Static get */
+    function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('User_location_prefs',$k,$v); }
+
+    /* the code above is auto generated do not remove the tag below */
+    ###END_AUTOCODE
+}
index 2cc37dbfefa2b327287eb6b3b1d1c9bcd8cadf48..35a000aacdcc715c5434966b226f343b7688fe70 100644 (file)
@@ -1,4 +1,3 @@
-
 [avatar]
 profile_id = 129
 original = 17
@@ -564,4 +563,14 @@ modified = 384
 
 [user_openid__keys]
 trustroot = K
-user_id = K
\ No newline at end of file
+user_id = K
+
+[user_location_prefs]
+user_id = 129
+share_location = 17
+created = 142
+modified = 384
+
+[user_location_prefs__keys]
+user_id = N
+
index 28ec3ec16b779eaf416b05448daf8d651d6940c4..d9c25bc723ade4610b69951a7dc9e6466e7e4391 100644 (file)
@@ -84,3 +84,13 @@ create table login_token (
 
     constraint primary key (user_id)
 ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+
+create table user_location_prefs (
+    user_id integer not null comment 'user who has the preference' references user (id),
+    share_location tinyint default 1 comment 'Whether to share location data',
+    created datetime not null comment 'date this record was created',
+    modified timestamp comment 'date this record was modified',
+
+    constraint primary key (user_id)
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+
index 6b3c2ca068a0339aff53a831e0c1818e3d00a716..94b03df639172146dff192e21634e2ff1db5362b 100644 (file)
@@ -587,3 +587,12 @@ create table login_token (
     constraint primary key (user_id)
 ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
 
+create table user_location_prefs (
+    user_id integer not null comment 'user who has the preference' references user (id),
+    share_location tinyint default 1 comment 'Whether to share location data',
+    created datetime not null comment 'date this record was created',
+    modified timestamp comment 'date this record was modified',
+
+    constraint primary key (user_id)
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+
index 42d4623b1ef9ea9558eca9f30213a459c801a1bd..8a70ed3fa15d0672899f0d4c22e68abf062b7349 100644 (file)
@@ -226,7 +226,8 @@ $default =
         'message' =>
         array('contentlimit' => null),
         'location' =>
-        array('namespace' => 1), // 1 = geonames, 2 = Yahoo Where on Earth
+        array('share' => 'user', // whether to share location; 'always', 'user', 'never'
+              'sharedefault' => true),
         'omb' =>
         array('timeout' => 5), // HTTP request timeout in seconds when contacting remote hosts for OMB updates
         'logincommand' =>
diff --git a/plugins/Blacklist/BlacklistPlugin.php b/plugins/Blacklist/BlacklistPlugin.php
new file mode 100644 (file)
index 0000000..655b092
--- /dev/null
@@ -0,0 +1,203 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Plugin to prevent use of nicknames or URLs on a blacklist
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Action
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2009 StatusNet Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * Plugin to prevent use of nicknames or URLs on a blacklist
+ *
+ * @category Plugin
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ */
+
+class BlacklistPlugin extends Plugin
+{
+    public $nicknames = array();
+    public $urls      = array();
+
+    /**
+     * Hook registration to prevent blacklisted homepages or nicknames
+     *
+     * Throws an exception if there's a blacklisted homepage or nickname.
+     *
+     * @param Action $action Action being called (usually register)
+     *
+     * @return boolean hook value
+     */
+
+    function onStartRegistrationTry($action)
+    {
+        $homepage = strtolower($action->trimmed('homepage'));
+
+        if (!empty($homepage)) {
+            if (!$this->_checkUrl($homepage)) {
+                $msg = sprintf(_m("You may not register with homepage '%s'"),
+                               $homepage);
+                throw new ClientException($msg);
+            }
+        }
+
+        $nickname = strtolower($action->trimmed('nickname'));
+
+        if (!empty($nickname)) {
+            if (!$this->_checkNickname($nickname)) {
+                $msg = sprintf(_m("You may not register with nickname '%s'"),
+                               $nickname);
+                throw new ClientException($msg);
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Hook profile update to prevent blacklisted homepages or nicknames
+     *
+     * Throws an exception if there's a blacklisted homepage or nickname.
+     *
+     * @param Action $action Action being called (usually register)
+     *
+     * @return boolean hook value
+     */
+
+    function onStartProfileSaveForm($action)
+    {
+        $homepage = strtolower($action->trimmed('homepage'));
+
+        if (!empty($homepage)) {
+            if (!$this->_checkUrl($homepage)) {
+                $msg = sprintf(_m("You may not use homepage '%s'"),
+                               $homepage);
+                throw new ClientException($msg);
+            }
+        }
+
+        $nickname = strtolower($action->trimmed('nickname'));
+
+        if (!empty($nickname)) {
+            if (!$this->_checkNickname($nickname)) {
+                $msg = sprintf(_m("You may not use nickname '%s'"),
+                               $nickname);
+                throw new ClientException($msg);
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Hook notice save to prevent blacklisted urls
+     *
+     * Throws an exception if there's a blacklisted url in the content.
+     *
+     * @param Notice &$notice Notice being saved
+     *
+     * @return boolean hook value
+     */
+
+    function onStartNoticeSave(&$notice)
+    {
+        common_replace_urls_callback($notice->content,
+                                     array($this, 'checkNoticeUrl'));
+        return true;
+    }
+
+    /**
+     * Helper callback for notice save
+     *
+     * Throws an exception if there's a blacklisted url in the content.
+     *
+     * @param string $url URL in the notice content
+     *
+     * @return boolean hook value
+     */
+
+    function checkNoticeUrl($url)
+    {
+        // It comes in special'd, so we unspecial it
+        // before comparing against patterns
+
+        $url = htmlspecialchars_decode($url);
+
+        if (!$this->_checkUrl($url)) {
+            $msg = sprintf(_m("You may not use url '%s' in notices"),
+                           $url);
+            throw new ClientException($msg);
+        }
+
+        return $url;
+    }
+
+    /**
+     * Helper for checking URLs
+     *
+     * Checks an URL against our patterns for a match.
+     *
+     * @param string $url URL to check
+     *
+     * @return boolean true means it's OK, false means it's bad
+     */
+
+    private function _checkUrl($url)
+    {
+        foreach ($this->urls as $pattern) {
+            if (preg_match("/$pattern/", $url)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Helper for checking nicknames
+     *
+     * Checks a nickname against our patterns for a match.
+     *
+     * @param string $nickname nickname to check
+     *
+     * @return boolean true means it's OK, false means it's bad
+     */
+
+    private function _checkNickname($nickname)
+    {
+        foreach ($this->nicknames as $pattern) {
+            if (preg_match("/$pattern/", $nickname)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+}
index 75dcca4fcb3eaba46b953cca453a39f448c542a3..0fca5f9cf90c22111773e667fe5e1654bb5f63c4 100644 (file)
@@ -27,7 +27,7 @@
  * @link      http://status.net/
  */
 
-if (!defined('STATUSNET') && !defined('LACONICA')) {
+if (!defined('STATUSNET')) {
     exit(1);
 }
 
@@ -43,6 +43,20 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
 
 class UserFlagPlugin extends Plugin
 {
+    const REVIEWFLAGS = 'UserFlagPlugin::reviewflags';
+    const CLEARFLAGS  = 'UserFlagPlugin::clearflags';
+
+    public $flagOnBlock = true;
+
+    /**
+     * Hook for ensuring our tables are created
+     *
+     * Ensures that the user_flag_profile table exists
+     * and has the right columns.
+     *
+     * @return boolean hook return
+     */
+
     function onCheckSchema()
     {
         $schema = Schema::get();
@@ -62,37 +76,61 @@ class UserFlagPlugin extends Plugin
         return true;
     }
 
-    function onInitializePlugin()
-    {
-        // XXX: do something here?
-        return true;
-    }
+    /**
+     * Add our actions to the URL router
+     *
+     * @param Net_URL_Mapper $m URL mapper for this hit
+     *
+     * @return boolean hook return
+     */
 
-    function onRouterInitialized($m) {
+    function onRouterInitialized($m)
+    {
         $m->connect('main/flag/profile', array('action' => 'flagprofile'));
+        $m->connect('main/flag/clear', array('action' => 'clearflag'));
         $m->connect('admin/profile/flag', array('action' => 'adminprofileflag'));
         return true;
     }
 
-   function onAutoload($cls)
+    /**
+     * Auto-load our classes if called
+     *
+     * @param string $cls Class to load
+     *
+     * @return boolean hook return
+     */
+
+    function onAutoload($cls)
     {
         switch ($cls)
         {
         case 'FlagprofileAction':
         case 'AdminprofileflagAction':
-            require_once(INSTALLDIR.'/plugins/UserFlag/' . strtolower(mb_substr($cls, 0, -6)) . '.php');
+        case 'ClearflagAction':
+            include_once INSTALLDIR.'/plugins/UserFlag/' .
+              strtolower(mb_substr($cls, 0, -6)) . '.php';
             return false;
         case 'FlagProfileForm':
-            require_once(INSTALLDIR.'/plugins/UserFlag/' . strtolower($cls . '.php'));
+        case 'ClearFlagForm':
+            include_once INSTALLDIR.'/plugins/UserFlag/' . strtolower($cls . '.php');
             return false;
         case 'User_flag_profile':
-            require_once(INSTALLDIR.'/plugins/UserFlag/'.$cls.'.php');
+            include_once INSTALLDIR.'/plugins/UserFlag/'.$cls.'.php';
             return false;
         default:
             return true;
         }
     }
 
+    /**
+     * Add a 'flag' button to profile page
+     *
+     * @param Action  &$action The action being called
+     * @param Profile $profile Profile being shown
+     *
+     * @return boolean hook result
+     */
+
     function onEndProfilePageActionsElements(&$action, $profile)
     {
         $user = common_current_user();
@@ -105,8 +143,8 @@ class UserFlagPlugin extends Plugin
                 $action->element('p', 'flagged', _('Flagged'));
             } else {
                 $form = new FlagProfileForm($action, $profile,
-                                        array('action' => 'showstream',
-                                              'nickname' => $profile->nickname));
+                                            array('action' => 'showstream',
+                                                  'nickname' => $profile->nickname));
                 $form->show();
             }
 
@@ -116,6 +154,14 @@ class UserFlagPlugin extends Plugin
         return true;
     }
 
+    /**
+     * Add a 'flag' button to profiles in a list
+     *
+     * @param ProfileListItem $item item being shown
+     *
+     * @return boolean hook result
+     */
+
     function onEndProfileListItemActionElements($item)
     {
         $user = common_current_user();
@@ -136,16 +182,78 @@ class UserFlagPlugin extends Plugin
         return true;
     }
 
+    /**
+     * Add our plugin's CSS to page output
+     *
+     * @param Action $action action being shown
+     *
+     * @return boolean hook result
+     */
+
     function onEndShowStatusNetStyles($action)
     {
-        $action->cssLink(common_path('plugins/UserFlag/userflag.css'), 
+        $action->cssLink(common_path('plugins/UserFlag/userflag.css'),
                          null, 'screen, projection, tv');
         return true;
     }
 
+    /**
+     * Initialize any flagging buttons on the page
+     *
+     * @param Action $action action being shown
+     *
+     * @return boolean hook result
+     */
+
     function onEndShowScripts($action)
     {
-        $action->inlineScript('if ($(".form_entity_flag").length > 0) { SN.U.FormXHR($(".form_entity_flag")); }');
+        $action->inlineScript('if ($(".form_entity_flag").length > 0) { '.
+                              'SN.U.FormXHR($(".form_entity_flag")); '.
+                              '}');
+        return true;
+    }
+
+    /**
+     * Check whether a user has one of our defined rights
+     *
+     * We define extra rights; this function checks to see if a
+     * user has one of them.
+     *
+     * @param User    $user    User being checked
+     * @param string  $right   Right we're checking
+     * @param boolean &$result out, result of the check
+     *
+     * @return boolean hook result
+     */
+
+    function onUserRightsCheck($user, $right, &$result)
+    {
+        switch ($right) {
+        case self::REVIEWFLAGS:
+        case self::CLEARFLAGS:
+            $result = $user->hasRole('moderator');
+            return false; // done processing!
+        }
+
+        return true; // unchanged!
+    }
+
+    /**
+     * Optionally flag profile when a block happens
+     *
+     * We optionally add a flag when a profile has been blocked
+     *
+     * @param User    $user    User doing the block
+     * @param Profile $profile Profile being blocked
+     *
+     * @return boolean hook result
+     */
+
+    function onEndBlockProfile($user, $profile)
+    {
+        if ($this->flagOnBlock) {
+            User_flag_profile::create($user->id, $profile->id);
+        }
         return true;
     }
 }
index 30bd4ae68de75aee12a7b5271ba653a909a54a08..658259452436aae780d56956ffe9841c7bf79391 100644 (file)
@@ -1,5 +1,15 @@
 <?php
-/*
+/**
+ * Data class for profile flags
+ *
+ * PHP version 5
+ *
+ * @category Data
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
  * StatusNet - the distributed open-source microblogging tool
  * Copyright (C) 2009, StatusNet, Inc.
  *
@@ -23,6 +33,18 @@ if (!defined('STATUSNET')) {
 
 require_once INSTALLDIR . '/classes/Memcached_DataObject.php';
 
+/**
+ * Data class for profile flags
+ *
+ * A class representing a user flagging another profile for review.
+ *
+ * @category Action
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ */
+
 class User_flag_profile extends Memcached_DataObject
 {
     ###START_AUTOCODE
@@ -40,7 +62,14 @@ class User_flag_profile extends Memcached_DataObject
     /* the code above is auto generated do not remove the tag below */
     ###END_AUTOCODE
 
-    function table() {
+    /**
+     * return table definition for DB_DataObject
+     *
+     * @return array array of column definitions
+     */
+
+    function table()
+    {
         return array(
                      'profile_id' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL,
                      'user_id'    => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL,
@@ -49,15 +78,39 @@ class User_flag_profile extends Memcached_DataObject
                      );
     }
 
-    function keys() {
+    /**
+     * return key definitions for DB_DataObject
+     *
+     * @return array key definitions
+     */
+
+    function keys()
+    {
         return array('profile_id' => 'N', 'user_id' => 'N');
     }
 
+    /**
+     * Get a single object with multiple keys
+     *
+     * @param array $kv Map of key-value pairs
+     *
+     * @return User_flag_profile found object or null
+     */
+
     function &pkeyGet($kv)
     {
         return Memcached_DataObject::pkeyGet('User_flag_profile', $kv);
     }
 
+    /**
+     * Check if a flag exists for given profile and user
+     *
+     * @param integer $profile_id Profile to check for
+     * @param integer $user_id    User to check for
+     *
+     * @return boolean true if exists, else false
+     */
+
     static function exists($profile_id, $user_id)
     {
         $ufp = User_flag_profile::pkeyGet(array('profile_id' => $profile_id,
@@ -65,4 +118,23 @@ class User_flag_profile extends Memcached_DataObject
 
         return !empty($ufp);
     }
+
+    static function create($user_id, $profile_id)
+    {
+        $ufp = new User_flag_profile();
+
+        $ufp->profile_id = $profile_id;
+        $ufp->user_id    = $user_id;
+        $ufp->created    = common_sql_now();
+
+        if (!$ufp->insert()) {
+            $msg = sprintf(_("Couldn't flag profile '%d' for review."),
+                           $profile_id);
+            throw new ServerException($msg);
+        }
+
+        $ufp->free();
+
+        return true;
+    }
 }
index 20b8086377de218535221f933c27d338c440f461..17374927b37ad970d733770083ed13e8cf718fdd 100644 (file)
@@ -43,6 +43,9 @@ if (!defined('STATUSNET')) {
 
 class AdminprofileflagAction extends Action
 {
+    var $page     = null;
+    var $profiles = null;
+
     /**
      * Take arguments for running
      *
@@ -55,6 +58,47 @@ class AdminprofileflagAction extends Action
     {
         parent::prepare($args);
 
+        $user = common_current_user();
+
+        // User must be logged in.
+
+        if (!common_logged_in()) {
+            $this->clientError(_('Not logged in.'));
+            return;
+        }
+
+        $user = common_current_user();
+
+        // ...because they're logged in
+
+        assert(!empty($user));
+
+        // It must be a "real" login, not saved cookie login
+
+        if (!common_is_real_login()) {
+            // Cookie theft is too easy; we require automatic
+            // logins to re-authenticate before admining the site
+            common_set_returnto($this->selfUrl());
+            if (Event::handle('RedirectToLogin', array($this, $user))) {
+                common_redirect(common_local_url('login'), 303);
+            }
+        }
+
+        // User must have the right to review flags
+
+        if (!$user->hasRight(UserFlagPlugin::REVIEWFLAGS)) {
+            $this->clientError(_('You cannot review profile flags.'));
+            return false;
+        }
+
+        $this->page = $this->trimmed('page');
+
+        if (empty($this->page)) {
+            $this->page = 1;
+        }
+
+        $this->profiles = $this->getProfiles();
+
         return true;
     }
 
@@ -73,7 +117,14 @@ class AdminprofileflagAction extends Action
         $this->showPage();
     }
 
-    function title() {
+    /**
+     * Title of this page
+     *
+     * @return string Title of the page
+     */
+
+    function title()
+    {
         return _('Flagged profiles');
     }
 
@@ -85,13 +136,20 @@ class AdminprofileflagAction extends Action
 
     function showContent()
     {
-        $profile = $this->getProfiles();
+        $pl = new FlaggedProfileList($this->profiles, $this);
 
-        $pl = new FlaggedProfileList($profile, $this);
+        $cnt = $pl->show();
 
-        $pl->show();
+        $this->pagination($this->page > 1, $cnt > PROFILES_PER_PAGE,
+                          $this->page, 'adminprofileflag');
     }
 
+    /**
+     * Retrieve this action's profiles
+     *
+     * @return Profile $profile Profile query results
+     */
+
     function getProfiles()
     {
         $ufp = new User_flag_profile();
@@ -103,7 +161,12 @@ class AdminprofileflagAction extends Action
         $ufp->whereAdd('cleared is NULL');
 
         $ufp->groupBy('profile_id');
-        $ufp->orderBy('flag_count DESC');
+        $ufp->orderBy('flag_count DESC, profile_id DESC');
+
+        $offset = ($this->page-1) * PROFILES_PER_PAGE;
+        $limit  = PROFILES_PER_PAGE + 1;
+
+        $ufp->limit($offset, $limit);
 
         $profiles = array();
 
@@ -122,7 +185,27 @@ class AdminprofileflagAction extends Action
     }
 }
 
-class FlaggedProfileList extends ProfileList {
+/**
+ * Specialization of ProfileList to show flagging information
+ *
+ * Most of the hard part is done in FlaggedProfileListItem.
+ *
+ * @category Widget
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ */
+
+class FlaggedProfileList extends ProfileList
+{
+    /**
+     * Factory method for creating new list items
+     *
+     * @param Profile $profile Profile to create an item for
+     *
+     * @return ProfileListItem newly-created item
+     */
 
     function newListItem($profile)
     {
@@ -130,11 +213,29 @@ class FlaggedProfileList extends ProfileList {
     }
 }
 
+/**
+ * Specialization of ProfileListItem to show flagging information
+ *
+ * @category Widget
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ */
+
 class FlaggedProfileListItem extends ProfileListItem
 {
-    var $user = null;
+    const MAX_FLAGGERS = 5;
+
+    var $user   = null;
     var $r2args = null;
 
+    /**
+     * Overload parent's action list with our own moderation-oriented buttons
+     *
+     * @return void
+     */
+
     function showActions()
     {
         $this->user = common_current_user();
@@ -159,6 +260,12 @@ class FlaggedProfileListItem extends ProfileListItem
         $this->endActions();
     }
 
+    /**
+     * Show a button to sandbox the profile
+     *
+     * @return void
+     */
+
     function showSandboxButton()
     {
         if ($this->user->hasRight(Right::SANDBOXUSER)) {
@@ -174,6 +281,12 @@ class FlaggedProfileListItem extends ProfileListItem
         }
     }
 
+    /**
+     * Show a button to silence the profile
+     *
+     * @return void
+     */
+
     function showSilenceButton()
     {
         if ($this->user->hasRight(Right::SILENCEUSER)) {
@@ -189,6 +302,12 @@ class FlaggedProfileListItem extends ProfileListItem
         }
     }
 
+    /**
+     * Show a button to delete user and profile
+     *
+     * @return void
+     */
+
     function showDeleteButton()
     {
 
@@ -200,7 +319,92 @@ class FlaggedProfileListItem extends ProfileListItem
         }
     }
 
+    /**
+     * Show a button to clear flags
+     *
+     * @return void
+     */
+
     function showClearButton()
     {
+        if ($this->user->hasRight(UserFlagPlugin::CLEARFLAGS)) {
+            $this->out->elementStart('li', 'entity_clear');
+            $cf = new ClearFlagForm($this->out, $this->profile, $this->r2args);
+            $cf->show();
+            $this->out->elementEnd('li');
+        }
+    }
+
+    /**
+     * Overload parent function to add flaggers list
+     *
+     * @return void
+     */
+
+    function endProfile()
+    {
+        $this->showFlaggersList();
+        parent::endProfile();
+    }
+
+    /**
+     * Show a list of people who've flagged this profile
+     *
+     * @return void
+     */
+
+    function showFlaggersList()
+    {
+        $flaggers = array();
+
+        $ufp = new User_flag_profile();
+
+        $ufp->selectAdd();
+        $ufp->selectAdd('user_id');
+        $ufp->profile_id = $this->profile->id;
+        $ufp->orderBy('created');
+
+        if ($ufp->find()) { // XXX: this should always happen
+            while ($ufp->fetch()) {
+                $user = User::staticGet('id', $ufp->user_id);
+                if (!empty($user)) { // XXX: this would also be unusual
+                    $flaggers[] = clone($user);
+                }
+            }
+        }
+
+        $cnt    = count($flaggers);
+        $others = 0;
+
+        if ($cnt > self::MAX_FLAGGERS) {
+            $flaggers = array_slice($flaggers, 0, self::MAX_FLAGGERS);
+            $others   = $cnt - self::MAX_FLAGGERS;
+        }
+
+        $lnks = array();
+
+        foreach ($flaggers as $flagger) {
+
+            $url = common_local_url('showstream',
+                                    array('nickname' => $flagger->nickname));
+
+            $lnks[] = XMLStringer::estring('a', array('href' => $url,
+                                                      'class' => 'flagger'),
+                                           $flagger->nickname);
+        }
+
+        if ($cnt > 0) {
+            $text = _('Flagged by ');
+
+            $text .= implode(', ', $lnks);
+
+            if ($others > 0) {
+                $text .= sprintf(_(' and %d others'), $others);
+            }
+
+            $this->out->elementStart('p', array('class' => 'flaggers'));
+            $this->out->raw($text);
+            $this->out->elementEnd('p');
+        }
     }
 }
diff --git a/plugins/UserFlag/clearflag.php b/plugins/UserFlag/clearflag.php
new file mode 100644 (file)
index 0000000..bd6732e
--- /dev/null
@@ -0,0 +1,138 @@
+<?php
+/**
+ * Clear all flags for a profile
+ *
+ * PHP version 5
+ *
+ * @category Action
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2009, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * Action to clear flags for a profile
+ *
+ * @category Action
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ */
+
+class ClearflagAction extends ProfileFormAction
+{
+    /**
+     * Take arguments for running
+     *
+     * @param array $args $_REQUEST args
+     *
+     * @return boolean success flag
+     */
+
+    function prepare($args)
+    {
+        if (!parent::prepare($args)) {
+            return false;
+        }
+
+        $user = common_current_user();
+
+        assert(!empty($user)); // checked above
+        assert(!empty($this->profile)); // checked above
+
+        return true;
+    }
+
+    /**
+     * Handle request
+     *
+     * Overriding the base Action's handle() here to deal check
+     * for Ajax and return an HXR response if necessary
+     *
+     * @param array $args $_REQUEST args; handled in prepare()
+     *
+     * @return void
+     */
+
+    function handle($args)
+    {
+        if ($_SERVER['REQUEST_METHOD'] == 'POST') {
+            $this->handlePost();
+            if (!$this->boolean('ajax')) {
+                $this->returnToArgs();
+            }
+        }
+    }
+
+    /**
+     * Handle POST
+     *
+     * Executes the actions; deletes all flags
+     *
+     * @return void
+     */
+
+    function handlePost()
+    {
+        $ufp = new User_flag_profile();
+
+        $result = $ufp->query('UPDATE user_flag_profile ' .
+                              'SET cleared = now() ' .
+                              'WHERE cleared is null ' .
+                              'AND profile_id = ' . $this->profile->id);
+
+        if ($result == false) {
+            $msg = sprintf(_("Couldn't clear flags for profile '%s'."),
+                           $this->profile->nickname);
+            throw new ServerException($msg);
+        }
+
+        $ufp->free();
+
+        if ($this->boolean('ajax')) {
+            $this->ajaxResults();
+        }
+    }
+
+    /**
+     * Return results in ajax form
+     *
+     * @return void
+     */
+
+    function ajaxResults()
+    {
+        header('Content-Type: text/xml;charset=utf-8');
+        $this->xw->startDocument('1.0', 'UTF-8');
+        $this->elementStart('html');
+        $this->elementStart('head');
+        $this->element('title', null, _('Flags cleared'));
+        $this->elementEnd('head');
+        $this->elementStart('body');
+        $this->element('p', 'cleared', _('Cleared'));
+        $this->elementEnd('body');
+        $this->elementEnd('html');
+    }
+}
diff --git a/plugins/UserFlag/clearflagform.php b/plugins/UserFlag/clearflagform.php
new file mode 100644 (file)
index 0000000..5ad6055
--- /dev/null
@@ -0,0 +1,92 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Form for clearing profile flags
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Form
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2009 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+require_once INSTALLDIR.'/lib/form.php';
+
+/**
+ * Form for clearing profile flags
+ *
+ * @category Form
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://status.net/
+ */
+
+class ClearFlagForm extends ProfileActionForm
+{
+    /**
+     * class of the form
+     * Action this form provides
+     *
+     * @return string class of the form
+     */
+
+    function formClass()
+    {
+        return 'form_entity_clearflag';
+    }
+
+    /**
+     * Action this form provides
+     *
+     * @return string Name of the action, lowercased.
+     */
+
+    function target()
+    {
+        return 'clearflag';
+    }
+
+    /**
+     * Title of the form
+     *
+     * @return string Title of the form, internationalized
+     */
+
+    function title()
+    {
+        return _('Clear');
+    }
+
+    /**
+     * Description of the form
+     *
+     * @return string description of the form, internationalized
+     */
+
+    function description()
+    {
+        return _('Clear all flags');
+    }
+}
index 9bce7865b8c973114d42b12dacb804473183c25f..2d0f0abb90f80ced2063b2a461e46c6022fdda27 100644 (file)
@@ -63,8 +63,7 @@ class FlagprofileAction extends ProfileFormAction
         assert(!empty($this->profile)); // checked above
 
         if (User_flag_profile::exists($this->profile->id,
-                                      $user->id))
-        {
+                                      $user->id)) {
             $this->clientError(_('Flag already exists.'));
             return false;
         }
@@ -72,7 +71,6 @@ class FlagprofileAction extends ProfileFormAction
         return true;
     }
 
-
     /**
      * Handle request
      *
@@ -107,25 +105,23 @@ class FlagprofileAction extends ProfileFormAction
         assert(!empty($user));
         assert(!empty($this->profile));
 
-        $ufp = new User_flag_profile();
-
-        $ufp->profile_id = $this->profile->id;
-        $ufp->user_id    = $user->id;
-        $ufp->created    = common_sql_now();
+        // throws an exception on error
 
-        if (!$ufp->insert()) {
-            throw new ServerException(sprintf(_("Couldn't flag profile '%s' for review."),
-                                              $this->profile->nickname));
-        }
-
-        $ufp->free();
+        User_flag_profile::create($user->id, $this->profile->id);
 
         if ($this->boolean('ajax')) {
             $this->ajaxResults();
         }
     }
 
-    function ajaxResults() {
+    /**
+     * Return results as AJAX message
+     *
+     * @return void
+     */
+
+    function ajaxResults()
+    {
         header('Content-Type: text/xml;charset=utf-8');
         $this->xw->startDocument('1.0', 'UTF-8');
         $this->elementStart('html');
old mode 100755 (executable)
new mode 100644 (file)