From cae2429d12321319d4a9ef1a22d73aaf76449a36 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Sat, 16 Jan 2010 19:44:37 +0000 Subject: [PATCH 001/170] JSLinting on JSON --- js/util.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/js/util.js b/js/util.js index 88016bd6d0..ef28f31e82 100644 --- a/js/util.js +++ b/js/util.js @@ -525,13 +525,13 @@ var SN = { // StatusNet $('#'+SN.C.S.NoticeDataGeo).attr('checked', true); var cookieValue = { - 'NLat': data.lat, - 'NLon': data.lon, - 'NLNS': lns, - 'NLID': lid, - 'NLN': NLN_text, - 'NLNU': location.url, - 'NDG': true + NLat: data.lat, + NLon: data.lon, + NLNS: lns, + NLID: lid, + NLN: NLN_text, + NLNU: location.url, + NDG: true }; $.cookie(SN.C.S.NoticeDataGeoCookie, JSON.stringify(cookieValue)); }); @@ -566,9 +566,9 @@ var SN = { // StatusNet $('#'+SN.C.S.NoticeLon).val(position.coords.longitude); var data = { - 'lat': position.coords.latitude, - 'lon': position.coords.longitude, - 'token': $('#token').val() + lat: position.coords.latitude, + lon: position.coords.longitude, + token: $('#token').val() }; getJSONgeocodeURL(geocodeURL, data); From f9cc3e6d942f4a1e3b9e000daa5ecca4df5e4ce5 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Sat, 16 Jan 2010 20:10:46 +0000 Subject: [PATCH 002/170] Added missing position paramater --- js/util.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/js/util.js b/js/util.js index ef28f31e82..373a4f3b01 100644 --- a/js/util.js +++ b/js/util.js @@ -494,7 +494,7 @@ var SN = { // StatusNet $.cookie(SN.C.S.NoticeDataGeoCookie, 'disabled'); } - function getJSONgeocodeURL(geocodeURL, data) { + function getJSONgeocodeURL(geocodeURL, data, position) { $.getJSON(geocodeURL, data, function(location) { var lns, lid; @@ -571,7 +571,7 @@ var SN = { // StatusNet token: $('#token').val() }; - getJSONgeocodeURL(geocodeURL, data); + getJSONgeocodeURL(geocodeURL, data, position); }, function(error) { @@ -598,7 +598,7 @@ var SN = { // StatusNet 'token': $('#token').val() }; - getJSONgeocodeURL(geocodeURL, data); + getJSONgeocodeURL(geocodeURL, data, position); } else { removeNoticeDataGeo(); From 2e643071ff3d03bd05f453b8e144aee4695e952e Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Sun, 17 Jan 2010 22:31:47 +0000 Subject: [PATCH 003/170] Took out focus out of textare when location share is enabled/disabled. Also avoids the conflict with the URL fragment on the conversation page. --- js/util.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/js/util.js b/js/util.js index 373a4f3b01..a10e9d15ae 100644 --- a/js/util.js +++ b/js/util.js @@ -624,8 +624,6 @@ var SN = { // StatusNet else { removeNoticeDataGeo(); } - - $('#'+SN.C.S.NoticeDataText).focus(); }).change(); } }, From a35c52b6bef775a47785922a25fad3d9199fce1c Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Mon, 18 Jan 2010 12:55:14 +0000 Subject: [PATCH 004/170] Some JS cleaning up for NoticeLocationAttach (which fixes also fixes a few bugs in WebKit) --- js/util.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/js/util.js b/js/util.js index a10e9d15ae..aeec8d89d7 100644 --- a/js/util.js +++ b/js/util.js @@ -494,7 +494,7 @@ var SN = { // StatusNet $.cookie(SN.C.S.NoticeDataGeoCookie, 'disabled'); } - function getJSONgeocodeURL(geocodeURL, data, position) { + function getJSONgeocodeURL(geocodeURL, data) { $.getJSON(geocodeURL, data, function(location) { var lns, lid; @@ -509,7 +509,7 @@ var SN = { // StatusNet } if (typeof(location.name) == 'undefined') { - NLN_text = position.coords.latitude + ';' + position.coords.longitude; + NLN_text = data.lat + ';' + data.lon; } else { NLN_text = location.name; @@ -571,7 +571,7 @@ var SN = { // StatusNet token: $('#token').val() }; - getJSONgeocodeURL(geocodeURL, data, position); + getJSONgeocodeURL(geocodeURL, data); }, function(error) { @@ -593,12 +593,12 @@ var SN = { // StatusNet else { if (NLat.length > 0 && NLon.length > 0) { var data = { - 'lat': NLat, - 'lon': NLon, - 'token': $('#token').val() + lat: NLat, + lon: NLon, + token: $('#token').val() }; - getJSONgeocodeURL(geocodeURL, data, position); + getJSONgeocodeURL(geocodeURL, data); } else { removeNoticeDataGeo(); From a7a0041a1157ad3ceec2ebd6af722a2529ad422f Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Fri, 22 Jan 2010 00:27:08 +0000 Subject: [PATCH 005/170] Quick hack to avoid breaking with geonames off when there's some old cookie state. This code's a little rough and tumble; any breakage halts JS execution and leaves the spinner going and no message submitted. --- js/util.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/js/util.js b/js/util.js index aeec8d89d7..a7339010a7 100644 --- a/js/util.js +++ b/js/util.js @@ -205,8 +205,10 @@ var SN = { // StatusNet cookieValue = JSON.parse(cookieValue); NLat = $('#'+SN.C.S.NoticeLat).val(cookieValue.NLat).val(); NLon = $('#'+SN.C.S.NoticeLon).val(cookieValue.NLon).val(); - NLNS = $('#'+SN.C.S.NoticeLocationNs).val(cookieValue.NLNS).val(); - NLID = $('#'+SN.C.S.NoticeLocationId).val(cookieValue.NLID).val(); + if ($('#'+SN.C.S.NoticeLocationNs).val(cookieValue.NLNS)) { + NLNS = $('#'+SN.C.S.NoticeLocationNs).val(cookieValue.NLNS).val(); + NLID = $('#'+SN.C.S.NoticeLocationId).val(cookieValue.NLID).val(); + } } if (cookieValue == 'disabled') { NDG = $('#'+SN.C.S.NoticeDataGeo).attr('checked', false).attr('checked'); @@ -301,8 +303,10 @@ var SN = { // StatusNet $('#'+SN.C.S.NoticeLat).val(NLat); $('#'+SN.C.S.NoticeLon).val(NLon); - $('#'+SN.C.S.NoticeLocationNs).val(NLNS); - $('#'+SN.C.S.NoticeLocationId).val(NLID); + if ($('#'+SN.C.S.NoticeLocationNs)) { + $('#'+SN.C.S.NoticeLocationNs).val(NLNS); + $('#'+SN.C.S.NoticeLocationId).val(NLID); + } $('#'+SN.C.S.NoticeDataGeo).attr('checked', NDG); } }); From 591c5037a745f1b620b213ddaa84c82c64201b71 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Mon, 25 Jan 2010 14:55:04 +0000 Subject: [PATCH 006/170] An update to geolocation cookie to use a single file and set the expiry date to 30 days from now. --- js/util.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/js/util.js b/js/util.js index a7339010a7..8d52d859b7 100644 --- a/js/util.js +++ b/js/util.js @@ -495,7 +495,7 @@ var SN = { // StatusNet $('#'+SN.C.S.NoticeLocationId).val(''); $('#'+SN.C.S.NoticeDataGeo).attr('checked', false); - $.cookie(SN.C.S.NoticeDataGeoCookie, 'disabled'); + $.cookie(SN.C.S.NoticeDataGeoCookie, 'disabled', { path: '/', expires: SN.U.GetDateFromNow(30) }); } function getJSONgeocodeURL(geocodeURL, data) { @@ -537,7 +537,8 @@ var SN = { // StatusNet NLNU: location.url, NDG: true }; - $.cookie(SN.C.S.NoticeDataGeoCookie, JSON.stringify(cookieValue)); + + $.cookie(SN.C.S.NoticeDataGeoCookie, JSON.stringify(cookieValue), { path: '/', expires: SN.U.GetDateFromNow(30) }); }); } @@ -658,6 +659,13 @@ var SN = { // StatusNet } return false; }); + }, + + GetDateFromNow: function(days) { + var date = new Date(); + date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); + + return date; } }, From e9a155db733f5266816ae5904db1eace4af1e2df Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Sat, 16 Jan 2010 20:57:18 +0000 Subject: [PATCH 007/170] Using visibility:hidden instead of display:none for checkbox --- theme/base/css/display.css | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/theme/base/css/display.css b/theme/base/css/display.css index 2e4c88dfa3..893cc60b51 100644 --- a/theme/base/css/display.css +++ b/theme/base/css/display.css @@ -73,7 +73,7 @@ input.checkbox, input.radio { position:relative; top:2px; -left:0; +left:auto; border:0; } @@ -567,7 +567,8 @@ float:right; font-size:0.8em; } -.form_notice #notice_data-geo_wrap label { +.form_notice #notice_data-geo_wrap label, +.form_notice #notice_data-geo_wrap input { position:absolute; top:25px; right:4px; @@ -578,7 +579,7 @@ height:16px; display:block; } .form_notice #notice_data-geo_wrap input { -display:none; +visibility:hidden; } .form_notice #notice_data-geo_wrap label { font-weight:normal; From 83eef607f1f01256e7342b421bddbed1fc32d6a6 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Sun, 17 Jan 2010 14:04:47 +0000 Subject: [PATCH 008/170] Inline script for maxlength is deprecated --- plugins/MobileProfile/MobileProfilePlugin.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/MobileProfile/MobileProfilePlugin.php b/plugins/MobileProfile/MobileProfilePlugin.php index 14d2500e8f..d426fc282b 100644 --- a/plugins/MobileProfile/MobileProfilePlugin.php +++ b/plugins/MobileProfile/MobileProfilePlugin.php @@ -356,8 +356,6 @@ class MobileProfilePlugin extends WAP20Plugin $contentLimit = Notice::maxContent(); - $form->out->inlineScript('maxLength = ' . $contentLimit . ';'); - if ($contentLimit > 0) { $form->out->element('div', array('id' => 'notice_text-count'), $contentLimit); From 14c3ec73b93ad59d5eb3947ac5a4b7ed6c26daad Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Sun, 17 Jan 2010 19:45:35 +0000 Subject: [PATCH 009/170] Updated UI for notice aside content and notice options in MobileProfile --- plugins/MobileProfile/mp-screen.css | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/plugins/MobileProfile/mp-screen.css b/plugins/MobileProfile/mp-screen.css index 3eefc0c8e0..472fbb0013 100644 --- a/plugins/MobileProfile/mp-screen.css +++ b/plugins/MobileProfile/mp-screen.css @@ -179,10 +179,12 @@ padding-bottom:4px; } .notice div.entry-content { margin-left:0; -width:62.5%; +width:75%; +max-width:100%; +min-width:0; } .notice-options { -width:34%; +width:50px; margin-right:1%; } @@ -190,12 +192,29 @@ margin-right:1%; width:16px; height:16px; } -.notice-options a, -.notice-options input { +.notice .notice-options a, +.notice .notice-options input { box-shadow:none; -moz-box-shadow:none; -webkit-box-shadow:none; } +.notice .notice-options a, +.notice .notice-options form { +margin:-4px 0 0 0; +} +.notice .notice-options .notice_repeat, +.notice .notice-options .notice_delete { +margin-top:18px; +} +.notice .notice-options .notice_reply, +.notice .notice-options .notice_repeat { +margin-left:18px; +} + + +.notice .notice-options .notice_delete { +float:left; +} .entity_profile { width:auto; From a93a3cc2701a0a948fe06b1d2a156c5d6b28c006 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Mon, 18 Jan 2010 11:12:05 +0000 Subject: [PATCH 010/170] Moved farbtastic's stylesheet to use relative paths for its own images --- {theme/base/css => js/farbtastic}/farbtastic.css | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename {theme/base/css => js/farbtastic}/farbtastic.css (70%) diff --git a/theme/base/css/farbtastic.css b/js/farbtastic/farbtastic.css similarity index 70% rename from theme/base/css/farbtastic.css rename to js/farbtastic/farbtastic.css index 7efcc73c3b..a88e7b868b 100644 --- a/theme/base/css/farbtastic.css +++ b/js/farbtastic/farbtastic.css @@ -16,17 +16,17 @@ height: 101px; } .farbtastic .wheel { - background: url(../../../js/farbtastic/wheel.png) no-repeat; + background: url(wheel.png) no-repeat; width: 195px; height: 195px; } .farbtastic .overlay { - background: url(../../../js/farbtastic/mask.png) no-repeat; + background: url(mask.png) no-repeat; } .farbtastic .marker { width: 17px; height: 17px; margin: -8px 0 0 -8px; overflow: hidden; - background: url(../../../js/farbtastic/marker.png) no-repeat; + background: url(marker.png) no-repeat; } From b1a4ccb4dfb0e815f4955ef0c5cde27d85437eb7 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Mon, 18 Jan 2010 11:29:05 +0000 Subject: [PATCH 011/170] Updated path to farbtastic stylesheet --- actions/designadminpanel.php | 2 +- lib/designsettings.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/actions/designadminpanel.php b/actions/designadminpanel.php index f862aff0eb..72ad6ade2a 100644 --- a/actions/designadminpanel.php +++ b/actions/designadminpanel.php @@ -289,7 +289,7 @@ class DesignadminpanelAction extends AdminPanelAction function showStylesheets() { parent::showStylesheets(); - $this->cssLink('css/farbtastic.css','base','screen, projection, tv'); + $this->cssLink('js/farbtastic/farbtastic.css',null,'screen, projection, tv'); } /** diff --git a/lib/designsettings.php b/lib/designsettings.php index b70ba0dfca..8e44c03a92 100644 --- a/lib/designsettings.php +++ b/lib/designsettings.php @@ -314,7 +314,7 @@ class DesignSettingsAction extends AccountSettingsAction function showStylesheets() { parent::showStylesheets(); - $this->cssLink('css/farbtastic.css','base','screen, projection, tv'); + $this->cssLink('js/farbtastic/farbtastic.css',null,'screen, projection, tv'); } /** From dbb44ca39a84181fa8ccfb66657075250507945c Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Mon, 18 Jan 2010 17:17:02 +0000 Subject: [PATCH 012/170] Missing null className for incoming email form legend --- actions/emailsettings.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actions/emailsettings.php b/actions/emailsettings.php index bfef2970da..08608348cd 100644 --- a/actions/emailsettings.php +++ b/actions/emailsettings.php @@ -130,7 +130,7 @@ class EmailsettingsAction extends AccountSettingsAction if (common_config('emailpost', 'enabled') && $user->email) { $this->elementStart('fieldset', array('id' => 'settings_email_incoming')); - $this->element('legend',_('Incoming email')); + $this->element('legend', null, _('Incoming email')); if ($user->incomingemail) { $this->elementStart('p'); $this->element('span', 'address', $user->incomingemail); From 1406cc452f212f629bdba8ac3e4422e907fbfb7f Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Wed, 20 Jan 2010 18:32:24 +0100 Subject: [PATCH 013/170] Updated notice item view where a) notice text no longer wraps around (under author's photo) b) supplemental notice content and options will start right under notice text. --- plugins/MobileProfile/mp-screen.css | 12 +++++++++++- theme/base/css/display.css | 22 +++++++++++++++++++--- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/plugins/MobileProfile/mp-screen.css b/plugins/MobileProfile/mp-screen.css index 472fbb0013..76071352d0 100644 --- a/plugins/MobileProfile/mp-screen.css +++ b/plugins/MobileProfile/mp-screen.css @@ -176,8 +176,18 @@ margin-bottom:0; .profile { padding-top:4px; padding-bottom:4px; +min-height:65px; } -.notice div.entry-content { +#content .notice .entry-title { +float:left; +width:100%; +margin-left:0; +} +#content .notice .author .photo { +position:static; +float:left; +} +#content .notice div.entry-content { margin-left:0; width:75%; max-width:100%; diff --git a/theme/base/css/display.css b/theme/base/css/display.css index 893cc60b51..7eff4709c7 100644 --- a/theme/base/css/display.css +++ b/theme/base/css/display.css @@ -957,6 +957,16 @@ float:left; #shownotice .vcard .photo { margin-bottom:4px; } +#content .notice .author .photo { +position:absolute; +top:11px; +left:0; +float:none; +} +#content .notice .entry-title { +margin-left:59px; +} + .vcard .url { text-decoration:none; } @@ -965,13 +975,19 @@ text-decoration:underline; } .notice .entry-title { -float:left; -width:100%; overflow:hidden; } .notice .entry-title.ov { overflow:visible; } +#showstream .notice .entry-title, +#showstream .notice div.entry-content { +margin-left:0; +} +#shownotice .notice .entry-title, +#shownotice .notice div.entry-content { +margin-left:110px; +} #shownotice .notice .entry-title { font-size:2.2em; } @@ -1001,7 +1017,6 @@ max-width:70%; } #showstream .notice div.entry-content, #shownotice .notice div.entry-content { -margin-left:0; max-width:79%; } @@ -1065,6 +1080,7 @@ position:relative; font-size:0.95em; width:113px; float:right; +margin-top:3px; margin-right:4px; } From 2f74a9bf148a22bb19524da290783cc0c10d38ba Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Wed, 20 Jan 2010 18:50:48 +0100 Subject: [PATCH 014/170] Better alignment for notice options in MobileProfile --- plugins/MobileProfile/mp-screen.css | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/plugins/MobileProfile/mp-screen.css b/plugins/MobileProfile/mp-screen.css index 76071352d0..04fa5fb002 100644 --- a/plugins/MobileProfile/mp-screen.css +++ b/plugins/MobileProfile/mp-screen.css @@ -194,7 +194,7 @@ max-width:100%; min-width:0; } .notice-options { -width:50px; +width:43px; margin-right:1%; } @@ -202,6 +202,13 @@ margin-right:1%; width:16px; height:16px; } +.notice-options form.processing { +background-image:none; +} +#wrap .notice-options form.processing input.submit { +background-position:0 47%; +} + .notice .notice-options a, .notice .notice-options input { box-shadow:none; @@ -212,16 +219,16 @@ box-shadow:none; .notice .notice-options form { margin:-4px 0 0 0; } -.notice .notice-options .notice_repeat, +.notice .notice-options .form_repeat, .notice .notice-options .notice_delete { -margin-top:18px; +margin-top:11px; } -.notice .notice-options .notice_reply, -.notice .notice-options .notice_repeat { -margin-left:18px; +.notice .notice-options .form_favor, +.notice .notice-options .form_disfavor, +.notice .notice-options .form_repeat { +margin-right:11px; } - .notice .notice-options .notice_delete { float:left; } From 72ae0ecc1c5e021b0f562fe5aa7ad0140d9c9ecf Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 21 Jan 2010 13:23:04 +0100 Subject: [PATCH 015/170] Removed mobile stylesheet from core output. If Mobile support is seeked, MobileProfile plugin should be used. --- lib/action.php | 4 - theme/base/css/mobile.css | 150 -------------------------------------- 2 files changed, 154 deletions(-) delete mode 100644 theme/base/css/mobile.css diff --git a/lib/action.php b/lib/action.php index 171bea17c7..e9207a66a6 100644 --- a/lib/action.php +++ b/lib/action.php @@ -199,10 +199,6 @@ class Action extends HTMLOutputter // lawsuit if (Event::handle('StartShowStatusNetStyles', array($this)) && Event::handle('StartShowLaconicaStyles', array($this))) { $this->cssLink('css/display.css',null,'screen, projection, tv'); - if (common_config('site', 'mobile')) { - // TODO: "handheld" CSS for other mobile devices - $this->cssLink('css/mobile.css','base','only screen and (max-device-width: 480px)'); // Mobile WebKit - } $this->cssLink('css/print.css','base','print'); Event::handle('EndShowStatusNetStyles', array($this)); Event::handle('EndShowLaconicaStyles', array($this)); diff --git a/theme/base/css/mobile.css b/theme/base/css/mobile.css deleted file mode 100644 index f6c53ea8dc..0000000000 --- a/theme/base/css/mobile.css +++ /dev/null @@ -1,150 +0,0 @@ -/** theme: base - * - * @package StatusNet - * @author Meitar Moscovitz - * @author Sarven Capadisli - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - */ - -body { -font-size:2.5em; -} - -#wrap { -width:95%; -} - -#header, -#header address, -#anon_notice, -#site_nav_local_views .nav, -#form_notice, -#form_notice .form_data li, -#core, -#content_inner, -#notices_primary, -.notice, -.notice .entry-title, -.notice div.entry-content, -.notice-options, -.notice .notice-options a, -.pagination, -.pagination .nav, -.aside .section { -float:none; -} - -.notice-options .notice_reply, -.notice-options .notice_delete, -.notice-options .form_favor, -.notice-options .form_disfavor { -position:static; -} - -#form_notice, -#anon_notice, -#footer, -#form_notice .form_actions input.submit { -width:auto; -} - -.form_settings label { -width:25%; -} -.form_settings .form_data p.form_guide { -margin-left:26%; -} - -#site_nav_global_primary { -width:75%; -} - -.entity_profile { -width:65%; -} -.entity_actions { -margin-left:0; -} - -#form_notice, -#anon_notice { -clear:both; -} - -#content, -#aside_primary { -width:96%; -padding-left:2%; -padding-right:2%; -} - -#site_notice { -position:static; -float:right; -clear:right; -width:75%; -margin-right:0; -margin-bottom:11px; -} - -.notices { -font-size:1.5em; -} - -#form_notice textarea { -width:80%; -height:5em; -} -#form_notice .form_note { -right:20%; -top:6em; -} - - -.vcard .photo, -.section .vcard .photo { -margin-right:18px; -} -.notice, -.profile { -margin-bottom:18px; -} - -.notices .entry-title, -.notices div.entry-content { -width:90%; -} -.notice div.entry-content { -margin-left:0; -} - -.notice .author .photo { -height:4.5em; -width:4.5em; -} -.notice-options { -position:absolute; -top:0; -right:0; -padding-left:7%; -width:3%; -} - -.notice-options .notice_delete a { -float:left; -} -.pagination .nav { -overflow:auto; -} - -#export_data { -display:none; -} - -#site_nav_local_views li { -margin-right:4px; -} -#site_nav_local_views a { -padding:18px 11px; -} From 6ae453bb7d7db232bead11c833d9447ee4e4ec0b Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Fri, 22 Jan 2010 19:18:14 +0100 Subject: [PATCH 016/170] Fixed innerHTML problem in IE7 and 8 for badge script --- js/identica-badge.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/identica-badge.js b/js/identica-badge.js index 8276f22a1d..e43d1c43c7 100644 --- a/js/identica-badge.js +++ b/js/identica-badge.js @@ -223,7 +223,7 @@ function markupPost(raw, server) { }, changeUserTo : function(el) { $.a.user = el.rel; - $.s.h.a.innerHTML = el.rev + $.a.headerText; + $.s.h.a.appendChild(document.createTextNode(el.rev + $.a.headerText)); $.s.h.a.href = 'http://' + $.a.server + '/' + el.id; $.f.runSearch(); }, From 23e96dd4d1f2378a62dc24f2f11b3e8b645bac1c Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Sun, 24 Jan 2010 15:34:40 +0100 Subject: [PATCH 017/170] Added version info for MobileProfile plugin --- plugins/MobileProfile/MobileProfilePlugin.php | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/plugins/MobileProfile/MobileProfilePlugin.php b/plugins/MobileProfile/MobileProfilePlugin.php index d426fc282b..5c913836dc 100644 --- a/plugins/MobileProfile/MobileProfilePlugin.php +++ b/plugins/MobileProfile/MobileProfilePlugin.php @@ -414,7 +414,15 @@ class MobileProfilePlugin extends WAP20Plugin return $proto.'://'.$serverpart.'/'.$pathpart.$relative; } + + function onPluginVersion(&$versions) + { + $versions[] = array('name' => 'MobileProfile', + 'version' => STATUSNET_VERSION, + 'author' => 'Sarven Capadisli', + 'homepage' => 'http://status.net/wiki/Plugin:MobileProfile', + 'rawdescription' => + _m('XHTML MobileProfile output for supporting user agents.')); + return true; + } } - - -?> From 9bacdb9134b39f97fd244bdc28d9e467ac05b61d Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Mon, 25 Jan 2010 22:44:05 +0100 Subject: [PATCH 018/170] Updated howto create a theme --- theme/README | 51 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/theme/README b/theme/README index 266a89fdf9..e154a723c4 100644 --- a/theme/README +++ b/theme/README @@ -2,37 +2,46 @@ * * @package StatusNet * @author Sarven Capadisli - * @copyright 2009 StatusNet, Inc. + * @copyright 2010 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/ */ -Location of key paths and files under theme/: +== Location of key paths and files == +

+base/css/
+base/css/display.css                #layout, typography rules
+base/images/                        #common icons, illustrations
+base/images/icons/icons-01.png      #main icons file (combined into a single file)
 
-./base/css/
-./base/css/display.css
-./base/images/
+default/css/
+default/css/display.css             #imports the base stylesheet for layout and adds background images and colour rules
+default/logo.png                    #default site logo for this theme
+default/mobilelogo.png              #default logo for the mobile output
+default/default-avatar-mini.png     #24x24 default avatar for minilists
+default/default-avatar-stream.png   #48x48 default avatar for notice timelines
+default/default-avatar-profile.png  #96x96 default avatar for the profile page
+
-./default/css/ -./default/css/display.css -./default/images/ -./base/display.css contains layout, typography rules: -Only alter this file if you want to change the layout of the site. Please note that, any updates to this in future statusnet releases may not be compatible with your version. +== How to create your own theme == -./default/css/display.css contains only the background images and colour rules: -This file is a good basis for creating your own theme. -Let's create a theme: +You probably want to do one of the following: -1. To start off, copy over the default theme: -cp -r default mytheme -2. Edit your mytheme stylesheet: -nano mytheme/css/display.css +* If you just want to change the text, link, background, content, sidebar colours, background image: +** Do this from the Admin->Design settings (recommended!). You could also create a directory and a file structure like the default theme, search and replace with your own values. This is more work, but, you can do this if you plan to make additional *minimal* changes. -a) Search and replace your colours and background images, or -b) Create your own layout either importing a separate stylesheet (e.g., change to @import url(base.css);) or simply place it before the rest of the rules. -4. Set /config.php to load 'mytheme': -$config['site']['theme'] = 'mytheme'; +* If you want to change the background images and colours: +# Create a directory and a file structure like the default theme. +# Have your stylesheet import base/css/display.css and add your own styles below. It is okay to add *minimal* changes here. + + +* If you want to create a different layout, typography, background images and colours: +** Create your own theme directory (base or default) with stylesheets and images like. + + +Finally, enable your theme by selecting it from the Admin->Design interface. You can set site's logo from here as well. + From 817f01c3b18ec626701de2a1402562eeb87eee6d Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Tue, 26 Jan 2010 01:58:10 +0100 Subject: [PATCH 019/170] Setting the geo location cookie expire date far into the future: 2029 ;) --- js/util.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/js/util.js b/js/util.js index 8d52d859b7..b864867fd1 100644 --- a/js/util.js +++ b/js/util.js @@ -495,7 +495,7 @@ var SN = { // StatusNet $('#'+SN.C.S.NoticeLocationId).val(''); $('#'+SN.C.S.NoticeDataGeo).attr('checked', false); - $.cookie(SN.C.S.NoticeDataGeoCookie, 'disabled', { path: '/', expires: SN.U.GetDateFromNow(30) }); + $.cookie(SN.C.S.NoticeDataGeoCookie, 'disabled', { path: '/', expires: SN.U.GetFullYear(2029, 0, 1) }); } function getJSONgeocodeURL(geocodeURL, data) { @@ -538,7 +538,7 @@ var SN = { // StatusNet NDG: true }; - $.cookie(SN.C.S.NoticeDataGeoCookie, JSON.stringify(cookieValue), { path: '/', expires: SN.U.GetDateFromNow(30) }); + $.cookie(SN.C.S.NoticeDataGeoCookie, JSON.stringify(cookieValue), { path: '/', expires: SN.U.GetFullYear(2029, 0, 1) }); }); } @@ -661,9 +661,9 @@ var SN = { // StatusNet }); }, - GetDateFromNow: function(days) { + GetFullYear: function(year, month, day) { var date = new Date(); - date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); + date.setFullYear(year, month, day); return date; } From cebab436a16b392d3854d6abe652a063e055b8c7 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Tue, 26 Jan 2010 19:13:05 +0100 Subject: [PATCH 020/170] Updated geolocation sharing in notice form for Realtime pop --- plugins/Realtime/realtimeupdate.css | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/Realtime/realtimeupdate.css b/plugins/Realtime/realtimeupdate.css index 56f869354d..31e7c2ae66 100644 --- a/plugins/Realtime/realtimeupdate.css +++ b/plugins/Realtime/realtimeupdate.css @@ -18,7 +18,8 @@ display:none; } .realtime-popup #form_notice label[for=notice_data-attach], -.realtime-popup #form_notice #notice_data-attach { +.realtime-popup #form_notice #notice_data-attach, +.realtime-popup #form_notice label[for=notice_data-geo] { top:0; } From 2e0297bb6ff0fd2d3a37d811bd3305ce5d6a77ee Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Wed, 27 Jan 2010 15:08:33 +0000 Subject: [PATCH 021/170] Better alignment for notice in shownotice page --- theme/base/css/display.css | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/theme/base/css/display.css b/theme/base/css/display.css index 7eff4709c7..d4c4716742 100644 --- a/theme/base/css/display.css +++ b/theme/base/css/display.css @@ -984,12 +984,13 @@ overflow:visible; #showstream .notice div.entry-content { margin-left:0; } -#shownotice .notice .entry-title, -#shownotice .notice div.entry-content { -margin-left:110px; -} #shownotice .notice .entry-title { +margin-left:110px; font-size:2.2em; +min-height:123px; +} +#shownotice .notice div.entry-content { +margin-left:0; } .notice p.entry-content { From b1402896e7dac59ab1eecae4babf83a06d2f256d Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 27 Jan 2010 09:13:21 -0800 Subject: [PATCH 022/170] Set default 24-hour expiry on Memcached objects where not specified. --- plugins/MemcachePlugin.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/plugins/MemcachePlugin.php b/plugins/MemcachePlugin.php index 8c8b8da6dc..2bc4b892bd 100644 --- a/plugins/MemcachePlugin.php +++ b/plugins/MemcachePlugin.php @@ -59,6 +59,8 @@ class MemcachePlugin extends Plugin public $persistent = null; + public $defaultExpiry = 86400; // 24h + /** * Initialize the plugin * @@ -110,6 +112,9 @@ class MemcachePlugin extends Plugin function onStartCacheSet(&$key, &$value, &$flag, &$expiry, &$success) { $this->_ensureConn(); + if ($expiry === null) { + $expiry = $this->defaultExpiry; + } $success = $this->_conn->set($key, $value, $flag, $expiry); Event::handle('EndCacheSet', array($key, $value, $flag, $expiry)); From 00d9b215f4de97f94035880041179879fcf0d201 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Wed, 27 Jan 2010 17:29:36 -0500 Subject: [PATCH 023/170] Plugin to support Google Adsense A plugin to easily add Google Adsense blocks to a StatusNet site. --- plugins/Adsense/AdsensePlugin.php | 160 ++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 plugins/Adsense/AdsensePlugin.php diff --git a/plugins/Adsense/AdsensePlugin.php b/plugins/Adsense/AdsensePlugin.php new file mode 100644 index 0000000000..dc2b32bc8f --- /dev/null +++ b/plugins/Adsense/AdsensePlugin.php @@ -0,0 +1,160 @@ +. + * + * @category Ads + * @package StatusNet + * @author Evan Prodromou + * @copyright 2010 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 add Google Adsense to StatusNet sites + * + * This plugin lets you add Adsense ad units to your StatusNet site. + * + * We support the 4 ad sizes for the Universal Ad Platform (UAP): + * + * Medium Rectangle + * (Small) Rectangle + * Leaderboard + * Wide Skyscraper + * + * They fit in different places on the default theme. Some themes + * might interact quite poorly with this plugin. + * + * To enable advertising, you must sign up with Google Adsense and + * get a client ID. + * + * https://www.google.com/adsense/ + * + * You'll also need to create an Adsense for Content unit in one + * of the four sizes described above. At the end of the process, + * note the "google_ad_client" and "google_ad_slot" values in the + * resultant Javascript. + * + * Add the plugin to config.php like so: + * + * addPlugin('Adsense', array('client' => 'Your client ID', + * 'rectangle' => 'slot')); + * + * Here, your client ID is the value of google_ad_client and the + * slot is the value of google_ad_slot. Note that if you create + * a different size, you'll need to provide different arguments: + * 'mediumRectangle', 'leaderboard', or 'wideSkyscraper'. + * + * If for some reason your ad server is different from the default, + * use the 'adScript' parameter to set the full path to the ad script. + * + * @category Plugin + * @package StatusNet + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + * + * @seeAlso UAPPlugin + */ + +class AdsensePlugin extends UAPPlugin +{ + public $adScript = 'http://pagead2.googlesyndication.com/pagead/show_ads.js'; + public $client = null; + + /** + * Show a medium rectangle 'ad' + * + * @param Action $action Action being shown + * + * @return void + */ + + protected function showMediumRectangle($action) + { + $this->showAdsenseCode($action, 300, 250, $this->mediumRectangle); + } + + /** + * Show a rectangle 'ad' + * + * @param Action $action Action being shown + * + * @return void + */ + + protected function showRectangle($action) + { + $this->showAdsenseCode($action, 180, 150, $this->rectangle); + } + + /** + * Show a wide skyscraper ad + * + * @param Action $action Action being shown + * + * @return void + */ + + protected function showWideSkyscraper($action) + { + $this->showAdsenseCode($action, 160, 600, $this->wideSkyscraper); + } + + /** + * Show a leaderboard ad + * + * @param Action $action Action being shown + * + * @return void + */ + + protected function showLeaderboard($action) + { + $this->showAdsenseCode($action, 728, 90, $this->leaderboard); + } + + /** + * Output the bits of JavaScript code to show Adsense + * + * @param Action $action Action being shown + * @param integer $width Width of the block + * @param integer $height Height of the block + * @param string $slot Slot identifier + * + * @return void + */ + + protected function showAdsenseCode($action, $width, $height, $slot) + { + $code = 'google_ad_client = "'.$this->client.'"; '; + $code .= 'google_ad_slot = "'.$slot.'"; '; + $code .= 'google_ad_width = "'.$width.'"; '; + $code .= 'google_ad_height = "'.$height.'"; '; + + $action->inlineScript($code); + + $action->script($this->adScript); + } +} \ No newline at end of file From 2aba2eeeaf78f4e407abe23643ff6027b0ea53dd Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Wed, 27 Jan 2010 17:55:33 -0500 Subject: [PATCH 024/170] width and height should be integers in AdsensePlugin --- plugins/Adsense/AdsensePlugin.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/Adsense/AdsensePlugin.php b/plugins/Adsense/AdsensePlugin.php index dc2b32bc8f..ab2b9a6fb3 100644 --- a/plugins/Adsense/AdsensePlugin.php +++ b/plugins/Adsense/AdsensePlugin.php @@ -150,8 +150,8 @@ class AdsensePlugin extends UAPPlugin { $code = 'google_ad_client = "'.$this->client.'"; '; $code .= 'google_ad_slot = "'.$slot.'"; '; - $code .= 'google_ad_width = "'.$width.'"; '; - $code .= 'google_ad_height = "'.$height.'"; '; + $code .= 'google_ad_width = '.$width.'; '; + $code .= 'google_ad_height = '.$height.'; '; $action->inlineScript($code); From 06cd3358970bfc27ff027cd1d8dc6974950c3793 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 27 Jan 2010 10:08:24 -0800 Subject: [PATCH 025/170] Add scripts/sendemail.php to send email to a user's address. Updated setup_status_network.sh to create a user with the site's nick, accept site tags, and send a mail to the user (if a template is set) Email and tag params added to the end: setup_status_net.sh mysite 'My Site' 'owner@example.com' '1user' (If multiple tags are needed, separate them with a pipe "|". Be sure to quote properly!) New parameters for setup.cfg need to be set: export PHPBASE=/var/www/statusnet export WILDCARD=example.net export MAILTEMPLATE=/etc/statusnet/newsite-mail.txt export MAILSUBJECT="Your new StatusNet site" $PHPBASE is the base dir for a callable StatusNet install, used to run command-line scripts for user setup. $WILDCARD is the wildcard domain, needed to build a full server name to pass into command-line scripts. $MAILTEMPLATE points to a file containing an e-mail message template. '$nickname', '$sitename', and '$userpass' can be used in the template for substitution. $MAILSUBJECT is the subject line for said email. To skip sending an email on creation, leave $MAILTEMPLATE blank or point to a non-existing file. --- scripts/sendemail.php | 82 +++++++++++++++++++++++++++++++++ scripts/setup.cfg.sample | 5 +- scripts/setup_status_network.sh | 50 ++++++++++++++++++-- 3 files changed, 132 insertions(+), 5 deletions(-) create mode 100755 scripts/sendemail.php diff --git a/scripts/sendemail.php b/scripts/sendemail.php new file mode 100755 index 0000000000..436e085bed --- /dev/null +++ b/scripts/sendemail.php @@ -0,0 +1,82 @@ +#!/usr/bin/env php +. + */ + +define('INSTALLDIR', realpath(dirname(__FILE__) . '/..')); + +$shortoptions = 'i:n:'; +$longoptions = array('id=', 'nickname=', 'subject='); + +$helptext = << +Sends given email text to user. + + -i --id id of the user to query + -n --nickname nickname of the user to query + --subject mail subject line (required) + +END_OF_USEREMAIL_HELP; + +require_once INSTALLDIR.'/scripts/commandline.inc'; + +if (have_option('i', 'id')) { + $id = get_option_value('i', 'id'); + $user = User::staticGet('id', $id); + if (empty($user)) { + print "Can't find user with ID $id\n"; + exit(1); + } +} else if (have_option('n', 'nickname')) { + $nickname = get_option_value('n', 'nickname'); + $user = User::staticGet('nickname', $nickname); + if (empty($user)) { + print "Can't find user with nickname '$nickname'\n"; + exit(1); + } +} else { + print "You must provide a user by --id or --nickname\n"; + exit(1); +} + +if (empty($user->email)) { + // @fixme unconfirmed address? + print "No email registered for user '$user->nickname'\n"; + exit(1); +} + +if (!have_option('subject')) { + echo "You must provide a subject line for the mail in --subject='...' param.\n"; + exit(1); +} +$subject = get_option_value('subject'); + +if (posix_isatty(STDIN)) { + print "You must provide message input on stdin!\n"; + exit(1); +} +$body = file_get_contents('php://stdin'); + +print "Sending to $user->email..."; +if (mail_to_user($user, $subject, $body)) { + print " done\n"; +} else { + print " failed.\n"; + exit(1); +} + diff --git a/scripts/setup.cfg.sample b/scripts/setup.cfg.sample index 8d03b06f5e..a0f10b3528 100644 --- a/scripts/setup.cfg.sample +++ b/scripts/setup.cfg.sample @@ -11,4 +11,7 @@ export AVATARBASE=/var/www/avatar.example.net export BACKGROUNDBASE=/var/www/background.example.net export FILEBASE=/var/www/file.example.net export PWDGEN="pwgen 20" - +export PHPBASE=/var/www/statusnet +export WILDCARD=example.net +export MAILTEMPLATE=/etc/statusnet/newsite-mail.txt +export MAILSUBJECT="Your new StatusNet site" diff --git a/scripts/setup_status_network.sh b/scripts/setup_status_network.sh index 777711fb55..d468df3aeb 100755 --- a/scripts/setup_status_network.sh +++ b/scripts/setup_status_network.sh @@ -2,9 +2,22 @@ source /etc/statusnet/setup.cfg -export nickname=$1 -export sitename=$2 +# setup_status_net.sh mysite 'My Site' 'owner@example.com' '1user' +export nickname="$1" +export sitename="$2" +export email="$3" +export tags="$4" + +# Fixme: if this is changed later we need to update profile URLs +# for the created user. +export server="$nickname.$WILDCARD" + +# End-user info +export userpass=`$PWDGEN` +export roles="administrator moderator owner" + +# DB info export password=`$PWDGEN` export database=$nickname$DBBASE export username=$nickname$USERBASE @@ -21,8 +34,8 @@ mysql -h $DBHOST -u $ADMIN --password=$ADMINPASS $SITEDB << ENDOFCOMMANDS GRANT ALL ON $database.* TO '$username'@'localhost' IDENTIFIED BY '$password'; GRANT ALL ON $database.* TO '$username'@'%' IDENTIFIED BY '$password'; -INSERT INTO status_network (nickname, dbhost, dbuser, dbpass, dbname, sitename, created) -VALUES ('$nickname', '$DBHOSTNAME', '$username', '$password', '$database', '$sitename', now()); +INSERT INTO status_network (nickname, dbhost, dbuser, dbpass, dbname, sitename, created, tags) +VALUES ('$nickname', '$DBHOSTNAME', '$username', '$password', '$database', '$sitename', now(), '$tags'); ENDOFCOMMANDS @@ -30,3 +43,32 @@ for top in $AVATARBASE $FILEBASE $BACKGROUNDBASE; do mkdir $top/$nickname chmod a+w $top/$nickname done + +php $PHPBASE/scripts/registeruser.php \ + -s"$server" \ + -n"$nickname" \ + -w"$userpass" \ + -e"$email" + +for role in $roles +do + php $PHPBASE/scripts/userrole.php \ + -s"$server" \ + -n"$nickname" \ + -r"$role" +done + +if [ -f "$MAILTEMPLATE" ] +then + # fixme how safe is this? are sitenames sanitized? + cat $MAILTEMPLATE | \ + sed "s/\$nickname/$nickname/" | \ + sed "s/\$sitename/$sitename/" | \ + sed "s/\$userpass/$userpass/" | \ + php $PHPBASE/scripts/sendemail.php \ + -s"$server" \ + -n"$nickname" \ + --subject="$MAILSUBJECT" +else + echo "No mail template, not sending email." +fi From 0373ab6fa437309ab790d5de4d43e241ba510c93 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Wed, 27 Jan 2010 18:35:02 -0500 Subject: [PATCH 026/170] Plugin to enable OpenX ads --- plugins/OpenX/OpenXPlugin.php | 166 ++++++++++++++++++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 plugins/OpenX/OpenXPlugin.php diff --git a/plugins/OpenX/OpenXPlugin.php b/plugins/OpenX/OpenXPlugin.php new file mode 100644 index 0000000000..a0d02ec117 --- /dev/null +++ b/plugins/OpenX/OpenXPlugin.php @@ -0,0 +1,166 @@ +. + * + * @category Ads + * @package StatusNet + * @author Evan Prodromou + * @copyright 2010 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); +} + +$_OpenXPlugin_Script = <<<\/scr"+"ipt>"); +ENDOFSCRIPT; + +/** + * Plugin for OpenX Ad Server + * + * This plugin supports the OpenX ad server, http://www.openx.org/ + * + * We support the 4 ad sizes for the Universal Ad Platform (UAP): + * + * Medium Rectangle + * (Small) Rectangle + * Leaderboard + * Wide Skyscraper + * + * They fit in different places on the default theme. Some themes + * might interact quite poorly with this plugin. + * + * To enable advertising, you will need an OpenX server. You'll need + * to set up a "zone" for your StatusNet site that identifies a + * kind of ad you want to place (of the above 4 sizes). + * + * Add the plugin to config.php like so: + * + * addPlugin('OpenX', array('adScript' => 'full path to script', + * 'rectangle' => 1)); + * + * Here, the 'adScript' parameter is the full path to the OpenX + * ad script, like 'http://example.com/www/delivery/ajs.php'. Note + * that we don't do any magic to swap between HTTP and HTTPS, so + * if you want HTTPS, say so. + * + * The 'rectangle' parameter is the zone ID for that ad space on + * your site. If you've configured another size, try 'mediumRectangle', + * 'leaderboard', or 'wideSkyscraper'. + * + * If for some reason your ad server is different from the default, + * use the 'adScript' parameter to set the full path to the ad script. + * + * @category Ads + * @package StatusNet + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + * + * @seeAlso UAPPlugin + */ + +class OpenXPlugin extends UAPPlugin +{ + public $adScript = null; + + /** + * Show a medium rectangle 'ad' + * + * @param Action $action Action being shown + * + * @return void + */ + + protected function showMediumRectangle($action) + { + $this->showAd($action, $this->mediumRectangle); + } + + /** + * Show a rectangle 'ad' + * + * @param Action $action Action being shown + * + * @return void + */ + + protected function showRectangle($action) + { + $this->showAd($action, $this->rectangle); + } + + /** + * Show a wide skyscraper ad + * + * @param Action $action Action being shown + * + * @return void + */ + + protected function showWideSkyscraper($action) + { + $this->showAd($action, $this->wideSkyscraper); + } + + /** + * Show a leaderboard ad + * + * @param Action $action Action being shown + * + * @return void + */ + + protected function showLeaderboard($action) + { + $this->showAd($action, $this->leaderboard); + } + + /** + * Show an ad using OpenX + * + * @param integer $zone Zone to show + * + * @return void + */ + + protected function showAd($zone) + { + global $_OpenXPlugin_Script; + + $this->inlineScript(sprintf($_OpenXPlugin_Script, $this->adScript, $zone)); + return true; + } +} \ No newline at end of file From e7a5471287598902d4fefbb93f1247d8c025e66b Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 27 Jan 2010 15:37:18 -0800 Subject: [PATCH 027/170] add additional post-install shell script option for setup_status_network.sh to do any other site-specific setup --- scripts/setup.cfg.sample | 1 + scripts/setup_status_network.sh | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/scripts/setup.cfg.sample b/scripts/setup.cfg.sample index a0f10b3528..f247a3bcae 100644 --- a/scripts/setup.cfg.sample +++ b/scripts/setup.cfg.sample @@ -15,3 +15,4 @@ export PHPBASE=/var/www/statusnet export WILDCARD=example.net export MAILTEMPLATE=/etc/statusnet/newsite-mail.txt export MAILSUBJECT="Your new StatusNet site" +export POSTINSTALL=/etc/statusnet/morestuff.sh diff --git a/scripts/setup_status_network.sh b/scripts/setup_status_network.sh index d468df3aeb..ae392191f1 100755 --- a/scripts/setup_status_network.sh +++ b/scripts/setup_status_network.sh @@ -72,3 +72,9 @@ then else echo "No mail template, not sending email." fi + +if [ -f "$POSTINSTALL" ] +then + echo "Running $POSTINSTALL ..." + source "$POSTINSTALL" +fi From 97a1ef14d2bfd32ec14db57b56d861e9cad7d02d Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Wed, 27 Jan 2010 18:39:55 -0500 Subject: [PATCH 028/170] using an action for output in OpenX plugin --- plugins/OpenX/OpenXPlugin.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/plugins/OpenX/OpenXPlugin.php b/plugins/OpenX/OpenXPlugin.php index a0d02ec117..96ed82b447 100644 --- a/plugins/OpenX/OpenXPlugin.php +++ b/plugins/OpenX/OpenXPlugin.php @@ -151,16 +151,17 @@ class OpenXPlugin extends UAPPlugin /** * Show an ad using OpenX * - * @param integer $zone Zone to show + * @param Action $action Action being shown + * @param integer $zone Zone to show * * @return void */ - protected function showAd($zone) + protected function showAd($action, $zone) { global $_OpenXPlugin_Script; - $this->inlineScript(sprintf($_OpenXPlugin_Script, $this->adScript, $zone)); + $action->inlineScript(sprintf($_OpenXPlugin_Script, $this->adScript, $zone)); return true; } } \ No newline at end of file From dd413ff4faeada13571711a0e4cc51e5ab25f7cc Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Wed, 27 Jan 2010 18:44:46 -0500 Subject: [PATCH 029/170] move script into OpenXPlugin::showAd() so it works --- plugins/OpenX/OpenXPlugin.php | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/plugins/OpenX/OpenXPlugin.php b/plugins/OpenX/OpenXPlugin.php index 96ed82b447..59485f25d8 100644 --- a/plugins/OpenX/OpenXPlugin.php +++ b/plugins/OpenX/OpenXPlugin.php @@ -31,22 +31,6 @@ if (!defined('STATUSNET')) { exit(1); } -$_OpenXPlugin_Script = <<<\/scr"+"ipt>"); -ENDOFSCRIPT; - /** * Plugin for OpenX Ad Server * @@ -159,9 +143,23 @@ class OpenXPlugin extends UAPPlugin protected function showAd($action, $zone) { - global $_OpenXPlugin_Script; +$scr = <<<\/scr"+"ipt>"); +ENDOFSCRIPT; - $action->inlineScript(sprintf($_OpenXPlugin_Script, $this->adScript, $zone)); + $action->inlineScript(sprintf($scr, $this->adScript, $zone)); return true; } } \ No newline at end of file From fe531dfc6c9c700b5566db634a9078d4b4b87d87 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 27 Jan 2010 16:03:01 -0800 Subject: [PATCH 030/170] Shuffle params on setup_status_network; adding fullname and pushing tags up --- scripts/setup_status_network.sh | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/scripts/setup_status_network.sh b/scripts/setup_status_network.sh index ae392191f1..f502a169a2 100755 --- a/scripts/setup_status_network.sh +++ b/scripts/setup_status_network.sh @@ -2,12 +2,13 @@ source /etc/statusnet/setup.cfg -# setup_status_net.sh mysite 'My Site' 'owner@example.com' '1user' +# setup_status_net.sh mysite 'My Site' '1user' 'owner@example.com' 'Firsty McLastname' export nickname="$1" export sitename="$2" -export email="$3" -export tags="$4" +export tags="$3" +export email="$4" +export fullname="$5" # Fixme: if this is changed later we need to update profile URLs # for the created user. @@ -47,6 +48,7 @@ done php $PHPBASE/scripts/registeruser.php \ -s"$server" \ -n"$nickname" \ + -f"$fullname" \ -w"$userpass" \ -e"$email" From 420ae06faf033b799c677845045b50b259801dbd Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Thu, 28 Jan 2010 00:39:40 +0000 Subject: [PATCH 031/170] These API methods should return true for ->isReadOnly($args)! --- actions/apiaccountratelimitstatus.php | 17 ++++++++++++++++- actions/apifriendshipsexists.php | 15 +++++++++++++++ actions/apifriendshipsshow.php | 16 +++++++++++++++- actions/apigroupismember.php | 15 +++++++++++++++ actions/apigroupshow.php | 15 +++++++++++++++ actions/apihelptest.php | 15 +++++++++++++++ actions/apistatusnetconfig.php | 15 +++++++++++++++ actions/apistatusnetversion.php | 15 +++++++++++++++ actions/apiusershow.php | 15 +++++++++++++++ 9 files changed, 136 insertions(+), 2 deletions(-) diff --git a/actions/apiaccountratelimitstatus.php b/actions/apiaccountratelimitstatus.php index 1a5afd552c..f19e315bf8 100644 --- a/actions/apiaccountratelimitstatus.php +++ b/actions/apiaccountratelimitstatus.php @@ -105,7 +105,22 @@ class ApiAccountRateLimitStatusAction extends ApiBareAuthAction print json_encode($out); } - $this->endDocument($this->format); + $this->endDocument($this->format); + } + + /** + * Return true if read only. + * + * MAY override + * + * @param array $args other arguments + * + * @return boolean is read only action? + */ + + function isReadOnly($args) + { + return true; } } diff --git a/actions/apifriendshipsexists.php b/actions/apifriendshipsexists.php index c040b9f6ad..ca62b5f514 100644 --- a/actions/apifriendshipsexists.php +++ b/actions/apifriendshipsexists.php @@ -116,4 +116,19 @@ class ApiFriendshipsExistsAction extends ApiPrivateAuthAction } } + /** + * Return true if read only. + * + * MAY override + * + * @param array $args other arguments + * + * @return boolean is read only action? + */ + + function isReadOnly($args) + { + return true; + } + } diff --git a/actions/apifriendshipsshow.php b/actions/apifriendshipsshow.php index 73ecc9249a..f29e637137 100644 --- a/actions/apifriendshipsshow.php +++ b/actions/apifriendshipsshow.php @@ -87,7 +87,6 @@ class ApiFriendshipsShowAction extends ApiBareAuthAction return true; } - /** * Determines whether this API resource requires auth. Overloaded to look * return true in case source_id and source_screen_name are both empty @@ -165,4 +164,19 @@ class ApiFriendshipsShowAction extends ApiBareAuthAction } + /** + * Return true if read only. + * + * MAY override + * + * @param array $args other arguments + * + * @return boolean is read only action? + */ + + function isReadOnly($args) + { + return true; + } + } diff --git a/actions/apigroupismember.php b/actions/apigroupismember.php index 69ead0b531..97f8435614 100644 --- a/actions/apigroupismember.php +++ b/actions/apigroupismember.php @@ -119,4 +119,19 @@ class ApiGroupIsMemberAction extends ApiBareAuthAction } } + /** + * Return true if read only. + * + * MAY override + * + * @param array $args other arguments + * + * @return boolean is read only action? + */ + + function isReadOnly($args) + { + return true; + } + } diff --git a/actions/apigroupshow.php b/actions/apigroupshow.php index 7aa49b1bf3..95d6f95afa 100644 --- a/actions/apigroupshow.php +++ b/actions/apigroupshow.php @@ -149,4 +149,19 @@ class ApiGroupShowAction extends ApiPrivateAuthAction return null; } + /** + * Return true if read only. + * + * MAY override + * + * @param array $args other arguments + * + * @return boolean is read only action? + */ + + function isReadOnly($args) + { + return true; + } + } diff --git a/actions/apihelptest.php b/actions/apihelptest.php index 7b4017531c..d0e9e4926f 100644 --- a/actions/apihelptest.php +++ b/actions/apihelptest.php @@ -92,5 +92,20 @@ class ApiHelpTestAction extends ApiPrivateAuthAction } } + /** + * Return true if read only. + * + * MAY override + * + * @param array $args other arguments + * + * @return boolean is read only action? + */ + + function isReadOnly($args) + { + return true; + } + } diff --git a/actions/apistatusnetconfig.php b/actions/apistatusnetconfig.php index ab96f2e5f9..dc1ab8685b 100644 --- a/actions/apistatusnetconfig.php +++ b/actions/apistatusnetconfig.php @@ -138,5 +138,20 @@ class ApiStatusnetConfigAction extends ApiAction } } + /** + * Return true if read only. + * + * MAY override + * + * @param array $args other arguments + * + * @return boolean is read only action? + */ + + function isReadOnly($args) + { + return true; + } + } diff --git a/actions/apistatusnetversion.php b/actions/apistatusnetversion.php index 5109cd8062..d094807597 100644 --- a/actions/apistatusnetversion.php +++ b/actions/apistatusnetversion.php @@ -98,5 +98,20 @@ class ApiStatusnetVersionAction extends ApiPrivateAuthAction } } + /** + * Return true if read only. + * + * MAY override + * + * @param array $args other arguments + * + * @return boolean is read only action? + */ + + function isReadOnly($args) + { + return true; + } + } diff --git a/actions/apiusershow.php b/actions/apiusershow.php index a7fe0dcc1e..6c8fad49ba 100644 --- a/actions/apiusershow.php +++ b/actions/apiusershow.php @@ -123,4 +123,19 @@ class ApiUserShowAction extends ApiPrivateAuthAction } + /** + * Return true if read only. + * + * MAY override + * + * @param array $args other arguments + * + * @return boolean is read only action? + */ + + function isReadOnly($args) + { + return true; + } + } From 324590c46eba2900164d0487a2d525ea4e9f93b8 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Thu, 28 Jan 2010 00:41:44 +0000 Subject: [PATCH 032/170] Some adjustments to the way API auth works after merging testing and 0.9.x --- lib/apiauth.php | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/lib/apiauth.php b/lib/apiauth.php index ac5e997c78..d441014add 100644 --- a/lib/apiauth.php +++ b/lib/apiauth.php @@ -57,7 +57,6 @@ class ApiAuthAction extends ApiAction var $auth_user_password = null; var $access_token = null; var $oauth_source = null; - var $auth_user = null; /** * Take arguments for running, and output basic auth header if needed @@ -82,22 +81,27 @@ class ApiAuthAction extends ApiAction if (!empty($this->access_token)) { $this->checkOAuthRequest(); } else { - $this->checkBasicAuthUser(); + $this->checkBasicAuthUser(true); } } else { // Check to see if a basic auth user is there even // if one's not required - $this->checkBasicAuthUser(false); + if (empty($this->access_token)) { + $this->checkBasicAuthUser(false); + } } // Reject API calls with the wrong access level if ($this->isReadOnly($args) == false) { + + common_debug(get_class($this) . ' is not read-only!'); + if ($this->access != self::READ_WRITE) { - $msg = 'API resource requires read-write access, ' . - 'but you only have read access.'; + $msg = _('API resource requires read-write access, ' . + 'but you only have read access.'); $this->clientError($msg, 401, $this->format); exit; } @@ -176,7 +180,7 @@ class ApiAuthAction extends ApiAction ($this->access = self::READ_WRITE) ? 'read-write' : 'read-only' )); - return true; + return; } else { throw new OAuthException('Bad access token.'); } @@ -228,9 +232,14 @@ class ApiAuthAction extends ApiAction } else { + $user = common_check_user($this->auth_user_nickname, + $this->auth_user_password); + if (Event::handle('StartSetApiUser', array(&$user))) { - $this->auth_user = common_check_user($this->auth_user_nickname, - $this->auth_user_password); + + if (!empty($user)) { + $this->auth_user = $user; + } Event::handle('EndSetApiUser', array($user)); } @@ -239,18 +248,18 @@ class ApiAuthAction extends ApiAction $this->access = self::READ_WRITE; - if (empty($this->auth_user)) { + if (empty($this->auth_user) && $required) { // basic authentication failed list($proxy, $ip) = common_client_ip(); - common_log( - LOG_WARNING, - 'Failed API auth attempt, nickname = ' . - "$nickname, proxy = $proxy, ip = $ip." - ); - + $msg = sprintf(_('Failed API auth attempt, nickname = %1$s, ' . + 'proxy = %2$s, ip = %3$s'), + $this->auth_user_nickname, + $proxy, + $ip); + common_log(LOG_WARNING, $msg); $this->showAuthError(); exit; } From f296f04abdc545f03daf55676282a2ae7108f79e Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Thu, 28 Jan 2010 01:24:40 +0000 Subject: [PATCH 033/170] Remove debugging statement --- lib/apiauth.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/apiauth.php b/lib/apiauth.php index d441014add..c684a6caee 100644 --- a/lib/apiauth.php +++ b/lib/apiauth.php @@ -96,9 +96,6 @@ class ApiAuthAction extends ApiAction // Reject API calls with the wrong access level if ($this->isReadOnly($args) == false) { - - common_debug(get_class($this) . ' is not read-only!'); - if ($this->access != self::READ_WRITE) { $msg = _('API resource requires read-write access, ' . 'but you only have read access.'); From 07d50a012af70d9bd35a26f645d791ae7ba29986 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 27 Jan 2010 17:34:13 -0800 Subject: [PATCH 034/170] fix update script -- read the diff wrong and put a couple fields on wrong table (whoops) --- db/rc3torc4.sql | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/db/rc3torc4.sql b/db/rc3torc4.sql index 8342c4bc6f..917c1f1c41 100644 --- a/db/rc3torc4.sql +++ b/db/rc3torc4.sql @@ -15,7 +15,9 @@ alter table queue_item rename to queue_item_old; alter table queue_item_new rename to queue_item; alter table consumer - add consumer_secret varchar(255) not null comment 'secret value', + add consumer_secret varchar(255) not null comment 'secret value'; + +alter table token add verifier varchar(255) comment 'verifier string for OAuth 1.0a', add verified_callback varchar(255) comment 'verified callback URL for OAuth 1.0a'; From 5182cc686dacfcf64130b591a3b7ded8d4a0c9dc Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Thu, 28 Jan 2010 01:39:18 +0000 Subject: [PATCH 035/170] Numbered format specifiers --- actions/apioauthauthorize.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/actions/apioauthauthorize.php b/actions/apioauthauthorize.php index fa074c4e76..15c3a9dad5 100644 --- a/actions/apioauthauthorize.php +++ b/actions/apioauthauthorize.php @@ -303,8 +303,9 @@ class ApiOauthAuthorizeAction extends ApiOauthAction $access = ($this->app->access_type & Oauth_application::$writeAccess) ? 'access and update' : 'access'; - $msg = _("The application %s by %s would like " . - "the ability to %s your account data."); + $msg = _('The application %1$s by ' . + '%2$s would like the ability ' . + 'to %3$s your account data.'); $this->raw(sprintf($msg, $this->app->name, From 3abfb454a381806a0f237e1463d2ba9a8612c22a Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 27 Jan 2010 18:39:17 -0800 Subject: [PATCH 036/170] Adds an emergency switch so we can run inbox distribution at save time (bypassing 'distrib' queue) Set $config['queue']['inboxes'] = false to do so --- classes/Notice.php | 10 +++++++--- lib/default.php | 1 + 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/classes/Notice.php b/classes/Notice.php index 0966697e21..6b364fb5ce 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -326,9 +326,13 @@ class Notice extends Memcached_DataObject # XXX: someone clever could prepend instead of clearing the cache $notice->blowOnInsert(); - $qm = QueueManager::get(); - - $qm->enqueue($notice, 'distrib'); + if (common_config('queue', 'inboxes')) { + $qm = QueueManager::get(); + $qm->enqueue($notice, 'distrib'); + } else { + $handler = new DistribQueueHandler(); + $handler->handle($notice); + } return $notice; } diff --git a/lib/default.php b/lib/default.php index 10ea348640..c729193b52 100644 --- a/lib/default.php +++ b/lib/default.php @@ -87,6 +87,7 @@ $default = 'monitor' => null, // URL to monitor ping endpoint (work in progress) 'softlimit' => '90%', // total size or % of memory_limit at which to restart queue threads gracefully 'debug_memory' => false, // true to spit memory usage to log + 'inboxes' => true, // true to do inbox distribution & output queueing from in background via 'distrib' queue ), 'license' => array('type' => 'cc', # can be 'cc', 'allrightsreserved', 'private' From c805a5e51874aa231c728c14402542648edb24bf Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 27 Jan 2010 18:55:22 -0800 Subject: [PATCH 037/170] Update queue, translation notes in readme --- README | 37 ++++++++++++++++--------------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/README b/README index f83873ca84..da278f7412 100644 --- a/README +++ b/README @@ -2,8 +2,8 @@ README ------ -StatusNet 0.9.0 ("Stand") Beta 3 -20 Jan 2010 +StatusNet 0.9.0 ("Stand") Beta 4 +27 Jan 2010 This is the README file for StatusNet (formerly Laconica), the Open Source microblogging platform. It includes installation instructions, @@ -597,26 +597,19 @@ server is probably a good idea for high-volume sites. needs as a parameter the install path; if you run it from the StatusNet dir, "." should suffice. -This will run eight (for now) queue handlers: +This will run the queue handlers: +* queuedaemon.php - polls for queued items for inbox processing and + pushing out to OMB, SMS, XMPP, etc. * xmppdaemon.php - listens for new XMPP messages from users and stores - them as notices in the database. -* jabberqueuehandler.php - sends queued notices in the database to - registered users who should receive them. -* publicqueuehandler.php - sends queued notices in the database to - public feed listeners. -* ombqueuehandler.php - sends queued notices to OpenMicroBlogging - recipients on foreign servers. -* smsqueuehandler.php - sends queued notices to SMS-over-email addresses - of registered users. -* xmppconfirmhandler.php - sends confirmation messages to registered - users. + them as notices in the database; also pulls queued XMPP output from + queuedaemon.php to push out to clients. -Note that these queue daemons are pretty raw, and need your care. In -particular, they leak memory, and you may want to restart them on a -regular (daily or so) basis with a cron job. Also, if they lose -the connection to the XMPP server for too long, they'll simply die. It -may be a good idea to use a daemon-monitoring service, like 'monit', +These two daemons will automatically restart in most cases of failure +including memory leaks (if a memory_limit is set), but may still die +or behave oddly if they lose connections to the XMPP or queue servers. + +It may be a good idea to use a daemon-monitoring service, like 'monit', to check their status and keep them running. All the daemons write their process IDs (pids) to /var/run/ by @@ -626,7 +619,7 @@ daemons. Since version 0.8.0, it's now possible to use a STOMP server instead of our kind of hacky home-grown DB-based queue solution. See the "queues" config section below for how to configure to use STOMP. As of this -writing, the software has been tested with ActiveMQ ( +writing, the software has been tested with ActiveMQ. Sitemaps -------- @@ -712,10 +705,12 @@ subdirectory to add a new language to your system. You'll need to compile the ".po" files into ".mo" files, however. Contributions of translation information to StatusNet are very easy: -you can use the Web interface at http://status.net/pootle/ to add one +you can use the Web interface at TranslateWiki.net to add one or a few or lots of new translations -- or even new languages. You can also download more up-to-date .po files there, if you so desire. +For info on helping with translations, see http://status.net/wiki/Translations + Backups ------- From ee4ea3f3e1eb64df2dd3ba677f5201f8787482a8 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Wed, 27 Jan 2010 21:59:38 -0500 Subject: [PATCH 038/170] increment software beta version --- lib/common.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/common.php b/lib/common.php index ada48b339d..b4e4a653c8 100644 --- a/lib/common.php +++ b/lib/common.php @@ -22,7 +22,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } //exit with 200 response, if this is checking fancy from the installer if (isset($_REQUEST['p']) && $_REQUEST['p'] == 'check-fancy') { exit; } -define('STATUSNET_VERSION', '0.9.0beta3'); +define('STATUSNET_VERSION', '0.9.0beta4'); define('LACONICA_VERSION', STATUSNET_VERSION); // compatibility define('STATUSNET_CODENAME', 'Stand'); From 5c0560a7fc7d5e7bb9c91da7cc273d2dc89e32b2 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 27 Jan 2010 19:50:08 -0800 Subject: [PATCH 039/170] fix for fix for bad realtime JS load --- plugins/Realtime/RealtimePlugin.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/Realtime/RealtimePlugin.php b/plugins/Realtime/RealtimePlugin.php index 16e28e94d3..6c212453e4 100644 --- a/plugins/Realtime/RealtimePlugin.php +++ b/plugins/Realtime/RealtimePlugin.php @@ -87,7 +87,7 @@ class RealtimePlugin extends Plugin $scripts = $this->_getScripts(); foreach ($scripts as $script) { - $action->script(common_path($script)); + $action->script($script); } $user = common_current_user(); @@ -307,7 +307,7 @@ class RealtimePlugin extends Plugin function _getScripts() { - return array('plugins/Realtime/realtimeupdate.js'); + return array(common_path('plugins/Realtime/realtimeupdate.js')); } function _updateInitialize($timeline, $user_id) From f026ecec322e59899b5ce87abaa6d93738f67b9e Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 27 Jan 2010 19:50:08 -0800 Subject: [PATCH 040/170] fix for fix for bad realtime JS load --- plugins/Realtime/RealtimePlugin.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/Realtime/RealtimePlugin.php b/plugins/Realtime/RealtimePlugin.php index 16e28e94d3..6c212453e4 100644 --- a/plugins/Realtime/RealtimePlugin.php +++ b/plugins/Realtime/RealtimePlugin.php @@ -87,7 +87,7 @@ class RealtimePlugin extends Plugin $scripts = $this->_getScripts(); foreach ($scripts as $script) { - $action->script(common_path($script)); + $action->script($script); } $user = common_current_user(); @@ -307,7 +307,7 @@ class RealtimePlugin extends Plugin function _getScripts() { - return array('plugins/Realtime/realtimeupdate.js'); + return array(common_path('plugins/Realtime/realtimeupdate.js')); } function _updateInitialize($timeline, $user_id) From fbd52111e1cc68a1a2710701ae0a146bcce5580e Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 27 Jan 2010 19:58:33 -0800 Subject: [PATCH 041/170] fix notice -- drop unused return value of variable that isn't initialized :) thx @ g0 for the catch --- classes/Notice.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/classes/Notice.php b/classes/Notice.php index 6b364fb5ce..90e3e76ef3 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -1378,8 +1378,6 @@ class Notice extends Memcached_DataObject } $reply->free(); - - return $ids; } function clearRepeats() From 644c319f5ad78dbedb686cdfef38152cadf615b2 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 27 Jan 2010 19:58:33 -0800 Subject: [PATCH 042/170] fix notice -- drop unused return value of variable that isn't initialized :) thx @ g0 for the catch --- classes/Notice.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/classes/Notice.php b/classes/Notice.php index 6b364fb5ce..90e3e76ef3 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -1378,8 +1378,6 @@ class Notice extends Memcached_DataObject } $reply->free(); - - return $ids; } function clearRepeats() From c2c262e4b47f5a4965317148b66ff390f4fc85fd Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Thu, 28 Jan 2010 04:46:10 +0000 Subject: [PATCH 043/170] Move faceboookapp.js to the Facebook plugin --- plugins/Facebook/facebookaction.php | 4 +--- {js => plugins/Facebook}/facebookapp.js | 0 2 files changed, 1 insertion(+), 3 deletions(-) rename {js => plugins/Facebook}/facebookapp.js (100%) diff --git a/plugins/Facebook/facebookaction.php b/plugins/Facebook/facebookaction.php index 389e1ea81f..8437a705a7 100644 --- a/plugins/Facebook/facebookaction.php +++ b/plugins/Facebook/facebookaction.php @@ -89,7 +89,7 @@ class FacebookAction extends Action function showScripts() { - $this->script('facebookapp.js'); + $this->script(common_path('plugins/Facebook/facebookapp.js')); } /** @@ -397,8 +397,6 @@ class FacebookAction extends Action return; } - - } } diff --git a/js/facebookapp.js b/plugins/Facebook/facebookapp.js similarity index 100% rename from js/facebookapp.js rename to plugins/Facebook/facebookapp.js From 427ac3a3a6eafa9a82006e9f0a9c2ab19a09fb4b Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 27 Jan 2010 20:51:04 -0800 Subject: [PATCH 044/170] debug log line for control channel sub --- lib/stompqueuemanager.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/stompqueuemanager.php b/lib/stompqueuemanager.php index 19e8c49b5c..da70d9ae31 100644 --- a/lib/stompqueuemanager.php +++ b/lib/stompqueuemanager.php @@ -242,6 +242,7 @@ class StompQueueManager extends QueueManager parent::start($master); $this->_connect(); + common_log(LOG_INFO, "Subscribing to $this->control"); $this->con->subscribe($this->control); if ($this->sites) { foreach ($this->sites as $server) { From 7347381183bfee96db8b2f89a4ba0ce5d04f76e2 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 27 Jan 2010 21:42:13 -0800 Subject: [PATCH 045/170] Fix for Mapstraction plugin's zoomed map links Move definition of NICKNAME_FMT above plugin initialization but below loading of Validate package. A merge error when refactoring setup lead to this not being defined yet when plugins were initialized, causing the router setup in MapstractionPlugin which tried to use this constant to fail. Result was bogus links and if you hit the URL directly the action would be "unrecognized". --- lib/common.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/common.php b/lib/common.php index b4e4a653c8..b482464aac 100644 --- a/lib/common.php +++ b/lib/common.php @@ -115,6 +115,10 @@ function __autoload($cls) require_once 'Validate.php'; require_once 'markdown.php'; +// XXX: other formats here + +define('NICKNAME_FMT', VALIDATE_NUM.VALIDATE_ALPHA_LOWER); + require_once INSTALLDIR.'/lib/util.php'; require_once INSTALLDIR.'/lib/action.php'; require_once INSTALLDIR.'/lib/mail.php'; @@ -136,6 +140,3 @@ try { exit; } -// XXX: other formats here - -define('NICKNAME_FMT', VALIDATE_NUM.VALIDATE_ALPHA_LOWER); From 1ba8045a9b318fd19fb55d2c469bd23a3976c2e8 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 28 Jan 2010 01:24:00 -0500 Subject: [PATCH 046/170] set session cookie correctly --- lib/util.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/util.php b/lib/util.php index 4312f9876b..c6dc4b43aa 100644 --- a/lib/util.php +++ b/lib/util.php @@ -178,7 +178,7 @@ function common_ensure_session() } if (isset($id)) { session_id($id); - setcookie(session_name(), $id); + setcookie(session_name(), $id, 0, common_config('site', 'path')); } @session_start(); if (!isset($_SESSION['started'])) { From 65c4cff01c42a5c432db968cf6731104b8040134 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 27 Jan 2010 23:14:49 -0800 Subject: [PATCH 047/170] append '/' on cookie path for now (may still need some refactoring) --- lib/util.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/util.php b/lib/util.php index c6dc4b43aa..26a12871b9 100644 --- a/lib/util.php +++ b/lib/util.php @@ -178,7 +178,7 @@ function common_ensure_session() } if (isset($id)) { session_id($id); - setcookie(session_name(), $id, 0, common_config('site', 'path')); + setcookie(session_name(), $id, 0, common_config('site', 'path') . '/'); } @session_start(); if (!isset($_SESSION['started'])) { From 78fe76b058157670bf6c0ab5f3454733d465684e Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 27 Jan 2010 23:16:06 -0800 Subject: [PATCH 048/170] dropping the setcookie() call from common_ensure_session() since we're pretty sure it's unnecessary --- lib/util.php | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/util.php b/lib/util.php index 26a12871b9..dd8189a582 100644 --- a/lib/util.php +++ b/lib/util.php @@ -178,7 +178,6 @@ function common_ensure_session() } if (isset($id)) { session_id($id); - setcookie(session_name(), $id, 0, common_config('site', 'path') . '/'); } @session_start(); if (!isset($_SESSION['started'])) { From ffaaf9de4a1da25f6168c53a33b25683ae134c61 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 27 Jan 2010 23:51:22 -0800 Subject: [PATCH 049/170] Don't preemptively close existing DB connections for web views (needed to keep # of conns from going insane on multi-site queue daemons, so just doing for CLI) May, or may not, help with mystery session problems --- classes/Memcached_DataObject.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/Memcached_DataObject.php b/classes/Memcached_DataObject.php index 2cc6377f83..b60aa7911d 100644 --- a/classes/Memcached_DataObject.php +++ b/classes/Memcached_DataObject.php @@ -428,7 +428,7 @@ class Memcached_DataObject extends DB_DataObject // // WARNING WARNING if we end up actually using multiple DBs at a time // we'll need some fancier logic here. - if (!$exists && !empty($_DB_DATAOBJECT['CONNECTIONS'])) { + if (!$exists && !empty($_DB_DATAOBJECT['CONNECTIONS']) && php_sapi_name() == 'cli') { foreach ($_DB_DATAOBJECT['CONNECTIONS'] as $index => $conn) { if (!empty($conn)) { $conn->disconnect(); From dcce323d18d4b779d2456f32aa99dfac5e2b7520 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 28 Jan 2010 15:05:23 +0100 Subject: [PATCH 050/170] Removed unused variable assignment for avatar URL and added missing fn --- lib/noticelist.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/noticelist.php b/lib/noticelist.php index 78abf34a76..85c169716a 100644 --- a/lib/noticelist.php +++ b/lib/noticelist.php @@ -555,11 +555,8 @@ class NoticeListItem extends Widget $this->out->raw(_('Repeated by')); - $avatar = $repeater->getAvatar(AVATAR_MINI_SIZE); - $this->out->elementStart('a', $attrs); - - $this->out->element('span', 'nickname', $repeater->nickname); + $this->out->element('span', 'fn nickname', $repeater->nickname); $this->out->elementEnd('a'); $this->out->elementEnd('span'); From 5b1245a32a04de602196d2216b9d25ac32e0fd3b Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 28 Jan 2010 15:06:03 +0100 Subject: [PATCH 051/170] Removed avatar from repeat of username (matches noticelist) --- actions/showstream.php | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/actions/showstream.php b/actions/showstream.php index 90ff67073a..c529193860 100644 --- a/actions/showstream.php +++ b/actions/showstream.php @@ -291,23 +291,6 @@ class ProfileNoticeListItem extends NoticeListItem $this->out->elementStart('span', 'repeat'); - $this->out->elementStart('a', $attrs); - - $avatar = $this->profile->getAvatar(AVATAR_MINI_SIZE); - - $this->out->element('img', array('src' => ($avatar) ? - $avatar->displayUrl() : - Avatar::defaultImage(AVATAR_MINI_SIZE), - 'class' => 'avatar photo', - 'width' => AVATAR_MINI_SIZE, - 'height' => AVATAR_MINI_SIZE, - 'alt' => - ($this->profile->fullname) ? - $this->profile->fullname : - $this->profile->nickname)); - - $this->out->elementEnd('a'); - $text_link = XMLStringer::estring('a', $attrs, $this->profile->nickname); $this->out->raw(sprintf(_('Repeat of %s'), $text_link)); From c6f8b94fa91e3e5559a9b79766e7320b096b839e Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 28 Jan 2010 16:02:39 +0100 Subject: [PATCH 052/170] Showing processing indicator for form_repeat on submit instead of form --- theme/base/css/display.css | 11 ++++++++++- theme/default/css/display.css | 9 +++++---- theme/identica/css/display.css | 9 +++++---- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/theme/base/css/display.css b/theme/base/css/display.css index 65dd159900..3c51deb312 100644 --- a/theme/base/css/display.css +++ b/theme/base/css/display.css @@ -1127,8 +1127,17 @@ top:3px; } .dialogbox .submit_dialogbox { -text-indent:0; font-weight:bold; +text-indent:0; +min-width:46px; +} + +#wrap form.processing input.submit, +.entity_actions a.processing, +.dialogbox.processing .submit_dialogbox { +cursor:wait; +outline:none; +text-indent:-9999px; } .notice-options { diff --git a/theme/default/css/display.css b/theme/default/css/display.css index 3aebb239d3..06711850fc 100644 --- a/theme/default/css/display.css +++ b/theme/default/css/display.css @@ -196,11 +196,12 @@ background-color:transparent; } #wrap form.processing input.submit, -.entity_actions a.processing { +.entity_actions a.processing, +.dialogbox.processing .submit_dialogbox { background:#FFFFFF url(../../base/images/icons/icon_processing.gif) no-repeat 47% 47%; -cursor:wait; -text-indent:-9999px; -outline:none; +} +.notice-options .form_repeat.processing { +background-image:none; } #content { diff --git a/theme/identica/css/display.css b/theme/identica/css/display.css index 2818196c20..1ac96ab5be 100644 --- a/theme/identica/css/display.css +++ b/theme/identica/css/display.css @@ -196,11 +196,12 @@ background-color:transparent; } #wrap form.processing input.submit, -.entity_actions a.processing { +.entity_actions a.processing, +.dialogbox.processing .submit_dialogbox { background:#FFFFFF url(../../base/images/icons/icon_processing.gif) no-repeat 47% 47%; -cursor:wait; -text-indent:-9999px; -outline:none; +} +.notice-options .form_repeat.processing { +background-image:none; } #content { From 61114ceff7c4124519cb5aa59b4d06ec73127e9d Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 28 Jan 2010 16:39:20 +0100 Subject: [PATCH 053/170] Fixed layout for powered by statusnet in biz --- theme/biz/css/base.css | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/theme/biz/css/base.css b/theme/biz/css/base.css index 6357e55b4e..471ac580d9 100644 --- a/theme/biz/css/base.css +++ b/theme/biz/css/base.css @@ -224,6 +224,15 @@ font-weight:bold; address img + .fn { display:none; } +address .poweredby { +float:left; +clear:left; +display:block; +position:relative; +top:7px; +margin-right:-47px; +} + #header { width:100%; From d4289cb34e9aefe7ffea3ca506ea0223f7858cb6 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 28 Jan 2010 16:41:28 +0100 Subject: [PATCH 054/170] Fixed layout when ad plugin is on for biz --- theme/biz/css/base.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/theme/biz/css/base.css b/theme/biz/css/base.css index 471ac580d9..2d4ac85ba6 100644 --- a/theme/biz/css/base.css +++ b/theme/biz/css/base.css @@ -396,7 +396,7 @@ margin-bottom:1em; } #content { -width:51.009%; +width:50%; min-height:259px; padding:1.795%; float:left; From b969fba2f5de4edf43e514e23565e1a90f916d04 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 28 Jan 2010 16:49:33 +0100 Subject: [PATCH 055/170] Updated geo sharing styles for biz --- theme/biz/css/base.css | 21 +++++++++++++++++++++ theme/biz/css/display.css | 15 ++++++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/theme/biz/css/base.css b/theme/biz/css/base.css index 2d4ac85ba6..47845421a3 100644 --- a/theme/biz/css/base.css +++ b/theme/biz/css/base.css @@ -518,6 +518,27 @@ margin-bottom:0; line-height:1.618; } +.form_notice #notice_data-geo_wrap label, +.form_notice #notice_data-geo_wrap input { +position:absolute; +top:25px; +right:4px; +left:auto; +cursor:pointer; +width:16px; +height:16px; +display:block; +} +.form_notice #notice_data-geo_wrap input { +visibility:hidden; +} +.form_notice #notice_data-geo_wrap label { +font-weight:normal; +font-size:1em; +margin-bottom:0; +text-indent:-9999px; +} + /* entity_profile */ .entity_profile { position:relative; diff --git a/theme/biz/css/display.css b/theme/biz/css/display.css index 7ea4515769..7a53b02bfc 100644 --- a/theme/biz/css/display.css +++ b/theme/biz/css/display.css @@ -60,6 +60,13 @@ input.submit, color:#FFFFFF; } +.form_notice label[for=notice_data-geo] { +background-position:0 -1780px; +} +.form_notice label[for=notice_data-geo].checked { +background-position:0 -1846px; +} + a, #site_nav_local_views .current a, div.notice-options input, @@ -115,6 +122,12 @@ text-indent:-9999px; outline:none; } +.form_notice label[for=notice_data-geo] { +background-image:url(../../base/images/icons/icons-01.gif); +background-repeat:no-repeat; +background-color:transparent; +} + #content { box-shadow:5px 7px 7px rgba(194, 194, 194, 0.3); -moz-box-shadow:5px 7px 7px rgba(194, 194, 194, 0.3); @@ -130,7 +143,7 @@ border-color:#FFFFFF; background-color:#FFFFFF; } -#site_nav_local_views li { +#site_nav_local_views li.current { box-shadow:3px 7px 5px rgba(194, 194, 194, 0.5); -moz-box-shadow:3px 7px 5px rgba(194, 194, 194, 0.5); -webkit-box-shadow:3px 7px 5px rgba(194, 194, 194, 0.5); From e881888789be64576cb299611c80a0d191fa5682 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 28 Jan 2010 17:08:24 +0100 Subject: [PATCH 056/170] Updated biz theme to use the single icons file --- theme/biz/css/base.css | 20 +++--- theme/biz/css/display.css | 132 +++++++++++++++++++++++++++++--------- 2 files changed, 110 insertions(+), 42 deletions(-) diff --git a/theme/biz/css/base.css b/theme/biz/css/base.css index 47845421a3..4ce7b49caa 100644 --- a/theme/biz/css/base.css +++ b/theme/biz/css/base.css @@ -911,25 +911,21 @@ margin-right:11px; .notice-options a { float:left; } -.notice-options .notice_delete, .notice-options .notice_reply, +.notice-options .form_repeat, .notice-options .form_favor, -.notice-options .form_disfavor { -position:absolute; -top:0; +.notice-options .form_disfavor, +.notice-options .repeated { +float:left; +margin-left:14.2%; } .notice-options .form_favor, .notice-options .form_disfavor { -left:0; -} -.notice-options .notice_reply { -left:29px; -} -.notice-options .notice_delete { -right:0; +margin-left:0; } .notice-options input, -.notice-options a { +.notice-options a, +.notice-options .repeated { text-indent:-9999px; outline:none; } diff --git a/theme/biz/css/display.css b/theme/biz/css/display.css index 7a53b02bfc..4dfd25a996 100644 --- a/theme/biz/css/display.css +++ b/theme/biz/css/display.css @@ -108,26 +108,63 @@ color:#333333; #form_notice.warning #notice_text-count { color:#000000; } -#form_notice label[for=notice_data-attach] { -background:transparent url(../../base/images/icons/twotone/green/clip-01.gif) no-repeat 0 45%; +.form_notice label[for=notice_data-attach] { +background-position:0 -328px; } -#form_notice #notice_data-attach { +.form_notice #notice_data-attach { opacity:0; } -#wrap form.processing input.submit { -background:#FFFFFF url(../../base/images/icons/icon_processing.gif) no-repeat 47% 47%; -cursor:wait; -text-indent:-9999px; -outline:none; -} - -.form_notice label[for=notice_data-geo] { +.form_notice label[for=notice_data-attach], +#export_data li a.rss, +#export_data li a.atom, +#export_data li a.foaf, +.entity_edit a, +.entity_send-a-message a, +.entity_nudge p, +.form_user_nudge input.submit, +.form_user_block input.submit, +.form_user_unblock input.submit, +.form_group_block input.submit, +.form_group_unblock input.submit, +.form_make_admin input.submit, +.notice .attachment, +.notice-options .notice_reply, +.notice-options form.form_favor input.submit, +.notice-options form.form_disfavor input.submit, +.notice-options .notice_delete, +.notice-options form.form_repeat input.submit, +#new_group a, +.pagination .nav_prev a, +.pagination .nav_next a, +button.close, +.form_group_leave input.submit, +.form_user_unsubscribe input.submit, +.form_group_join input.submit, +.form_user_subscribe input.submit, +.entity_subscribe a, +.entity_moderation p, +.entity_sandbox input.submit, +.entity_silence input.submit, +.entity_delete input.submit, +.notice-options .repeated, +.form_notice label[for=notice_data-geo], +button.minimize, +.form_reset_key input.submit { background-image:url(../../base/images/icons/icons-01.gif); background-repeat:no-repeat; background-color:transparent; } +#wrap form.processing input.submit, +.entity_actions a.processing, +.dialogbox.processing .submit_dialogbox { +background:#FFFFFF url(../../base/images/icons/icon_processing.gif) no-repeat 47% 47%; +} +.notice-options .form_repeat.processing { +background-image:none; +} + #content { box-shadow:5px 7px 7px rgba(194, 194, 194, 0.3); -moz-box-shadow:5px 7px 7px rgba(194, 194, 194, 0.3); @@ -175,13 +212,13 @@ background-repeat:no-repeat; background-position:0 45%; } #export_data li a.rss { -background-image:url(../../base/images/icons/icon_rss.png); +background-position:0 -130px; } #export_data li a.atom { -background-image:url(../../base/images/icons/icon_atom.png); +background-position:0 -64px; } #export_data li a.foaf { -background-image:url(../../base/images/icons/icon_foaf.gif); +background-position:0 1px; } .entity_edit a, @@ -211,43 +248,65 @@ background-color:#87B4C8; } .entity_edit a { -background-image:url(../../base/images/icons/twotone/green/edit.gif); +background-position: 5px -718px; } .entity_send-a-message a { -background-image:url(../../base/images/icons/twotone/green/quote.gif); +background-position: 5px -852px; } + .entity_nudge p, .form_user_nudge input.submit { -background-image:url(../../base/images/icons/twotone/green/mail.gif); +background-position: 5px -785px; } .form_user_block input.submit, .form_user_unblock input.submit, .form_group_block input.submit, .form_group_unblock input.submit { -background-image:url(../../base/images/icons/twotone/green/shield.gif); +background-position: 5px -918px; } .form_make_admin input.submit { -background-image:url(../../base/images/icons/twotone/green/admin.gif); +background-position: 5px -983px; +} +.entity_moderation p { +background-position: 5px -1313px; +} +.entity_sandbox input.submit { +background-position: 5px -1380px; +} +.entity_silence input.submit { +background-position: 5px -1445px; +} +.entity_delete input.submit { +background-position: 5px -1511px; +} +.form_reset_key input.submit { +background-position: 5px -1973px; } /* NOTICES */ .notice .attachment { -background:transparent url(../../base/images/icons/twotone/green/clip-02.gif) no-repeat 0 45%; +background-position:0 -394px; } #attachments .attachment { background:none; } .notice-options .notice_reply { -background:transparent url(../../base/images/icons/twotone/green/reply.gif) no-repeat 0 45%; +background-position:0 -592px; } .notice-options form.form_favor input.submit { -background:transparent url(../../base/images/icons/twotone/green/favourite.gif) no-repeat 0 45%; +background-position:0 -460px; } .notice-options form.form_disfavor input.submit { -background:transparent url(../../base/images/icons/twotone/green/disfavourite.gif) no-repeat 0 45%; +background-position:0 -526px; } .notice-options .notice_delete { -background:transparent url(../../base/images/icons/twotone/green/trash.gif) no-repeat 0 45%; +background-position:0 -658px; +} +.notice-options form.form_repeat input.submit { +background-position:0 -1582px; +} +.notice-options .repeated { +background-position:0 -1648px; } .notices div.entry-content, @@ -284,19 +343,32 @@ background-color:rgba(200, 200, 200, 0.300); /*END: NOTICES */ #new_group a { -background:transparent url(../../base/images/icons/twotone/green/news.gif) no-repeat 0 45%; +background-position:0 -1054px; } .pagination .nav_prev a, .pagination .nav_next a { background-repeat:no-repeat; -border-color:#CEE1E9; +box-shadow:3px 3px 3px rgba(194, 194, 194, 0.3); +-moz-box-shadow:3px 3px 3px rgba(194, 194, 194, 0.3); +-webkit-box-shadow:3px 3px 3px rgba(194, 194, 194, 0.3); } .pagination .nav_prev a { -background-image:url(../../base/images/icons/twotone/green/arrow-left.gif); -background-position:10% 45%; +background-position:10% -187px; } .pagination .nav_next a { -background-image:url(../../base/images/icons/twotone/green/arrow-right.gif); -background-position:90% 45%; +background-position:105% -252px; +} +.pagination .nav .processing { +background-image:url(../../base/images/icons/icon_processing.gif); +box-shadow:none; +-moz-box-shadow:none; +-webkit-box-shadow:none; +outline:none; +} +.pagination .nav_next a.processing { +background-position:90% 47%; +} +.pagination .nav_prev a.processing { +background-position:10% 47%; } From 6e556e502a13a1cfef51492707932635322f4bc4 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 28 Jan 2010 17:09:35 +0100 Subject: [PATCH 057/170] Updated biz theme to hide form_repeat legend --- theme/biz/css/base.css | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/theme/biz/css/base.css b/theme/biz/css/base.css index 4ce7b49caa..ec8ca22f5f 100644 --- a/theme/biz/css/base.css +++ b/theme/biz/css/base.css @@ -942,17 +942,18 @@ padding-left:16px; width:16px; padding:2px 0; } +.notice-options .form_repeat legend, .notice-options .form_favor legend, .notice-options .form_disfavor legend { display:none; } +.notice-options .form_repeat fieldset, .notice-options .form_favor fieldset, .notice-options .form_disfavor fieldset { border:0; padding:0; } - #usergroups #new_group { float: left; margin-right: 2em; From 83087e9d9b14b4425368dbd2f95ff6fcc909e7f6 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 28 Jan 2010 17:12:48 +0100 Subject: [PATCH 058/170] Updated biz theme notice options --- theme/biz/css/base.css | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/theme/biz/css/base.css b/theme/biz/css/base.css index ec8ca22f5f..d5873b0b07 100644 --- a/theme/biz/css/base.css +++ b/theme/biz/css/base.css @@ -903,9 +903,10 @@ text-transform:lowercase; .notice-options { position:relative; font-size:0.95em; -width:90px; +width:113px; float:right; -margin-right:11px; +margin-top:3px; +margin-right:4px; } .notice-options a { @@ -936,11 +937,17 @@ border:0; .notice-options .notice_reply, .notice-options .notice_delete { text-decoration:none; -padding-left:16px; +} +.notice .notice-options .notice_delete { +float:right; } .notice-options form input.submit { width:16px; -padding:2px 0; +height:16px; +padding:0; +border-radius:0; +-moz-border-radius:0; +-webkit-border-radius:0; } .notice-options .form_repeat legend, .notice-options .form_favor legend, @@ -953,6 +960,11 @@ display:none; border:0; padding:0; } +.notice-options a, +.notice-options .repeated { +width:16px; +height:16px; +} #usergroups #new_group { float: left; From 156efda37a2a6d2e16b061180fea828581691560 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 28 Jan 2010 17:16:11 +0100 Subject: [PATCH 059/170] Updated biz theme to use dialogbox styles --- theme/biz/css/base.css | 43 ++++++++++++++++++++++++++++++++++++++ theme/biz/css/display.css | 44 ++++++++++++++++++++++++--------------- 2 files changed, 70 insertions(+), 17 deletions(-) diff --git a/theme/biz/css/base.css b/theme/biz/css/base.css index d5873b0b07..a8834ca570 100644 --- a/theme/biz/css/base.css +++ b/theme/biz/css/base.css @@ -900,6 +900,49 @@ display:inline-block; text-transform:lowercase; } +.dialogbox { +position:absolute; +top:-4px; +right:29px; +z-index:9; +min-width:199px; +float:none; +background-color:#FFF; +padding:11px; +border-radius:7px; +-moz-border-radius:7px; +-webkit-border-radius:7px; +border-style:solid; +border-width:1px; +border-color:#DDDDDD; +-moz-box-shadow:3px 7px 5px rgba(194, 194, 194, 0.7); +} + +.dialogbox legend { +display:block !important; +margin-right:18px; +} + +.dialogbox button.close { +position:absolute; +right:3px; +top:3px; +} + +.dialogbox .submit_dialogbox { +font-weight:bold; +text-indent:0; +min-width:46px; +} + +#wrap form.processing input.submit, +.entity_actions a.processing, +.dialogbox.processing .submit_dialogbox { +cursor:wait; +outline:none; +text-indent:-9999px; +} + .notice-options { position:relative; font-size:0.95em; diff --git a/theme/biz/css/display.css b/theme/biz/css/display.css index 4dfd25a996..52f36ab54a 100644 --- a/theme/biz/css/display.css +++ b/theme/biz/css/display.css @@ -40,25 +40,35 @@ border-color:#DDDDDD; background:none; } -input.submit, -#form_notice.warning #notice_text-count, -.form_settings .form_note, -.entity_remote_subscribe { -background-color:#9BB43E; -} - -input:focus, textarea:focus, select:focus, -#form_notice.warning #notice_data-text { -border-color:#9BB43E; -box-shadow:3px 3px 3px rgba(194, 194, 194, 0.3); --moz-box-shadow:3px 3px 3px rgba(194, 194, 194, 0.3); --webkit-box-shadow:3px 3px 3px rgba(194, 194, 194, 0.3); -} -input.submit, -.entity_remote_subscribe, -#site_nav_local_views a { +input.submit { color:#FFFFFF; } +.entity_actions input.submit { +border-color:transparent; +text-shadow:none; +} +.dialogbox .submit_dialogbox, +input.submit, +.form_notice input.submit { +background:#AAAAAA url(../../base/images/illustrations/illu_pattern-01.png) 0 0 repeat-x; +text-shadow:0 1px 0 #FFFFFF; +color:#000000; +border-color:#AAAAAA; +border-top-color:#CCCCCC; +border-left-color:#CCCCCC; +} +.dialogbox .submit_dialogbox:hover, +input.submit:hover { +background-position:0 -5px; +} +.dialogbox .submit_dialogbox:focus, +input.submit:focus { +background-position:0 -15px; +box-shadow:3px 3px 3px rgba(194, 194, 194, 0.1); +-moz-box-shadow:3px 3px 3px rgba(194, 194, 194, 0.1); +-webkit-box-shadow:3px 3px 3px rgba(194, 194, 194, 0.1); +text-shadow:none; +} .form_notice label[for=notice_data-geo] { background-position:0 -1780px; From d955fb5e374d94526f9af928d28514e7b9a27a8f Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 28 Jan 2010 17:22:01 +0100 Subject: [PATCH 060/170] Updated biz theme entity_actions styles --- theme/biz/css/base.css | 110 ++++++++++++++++++++++++++++---------- theme/biz/css/display.css | 41 +++++++------- 2 files changed, 104 insertions(+), 47 deletions(-) diff --git a/theme/biz/css/base.css b/theme/biz/css/base.css index a8834ca570..bd70c083e2 100644 --- a/theme/biz/css/base.css +++ b/theme/biz/css/base.css @@ -605,8 +605,9 @@ display:none; /*entity_actions*/ .entity_actions { float:right; -margin-left:4.35%; -max-width:25%; +margin-left:2%; +margin-bottom:18px; +min-width:21%; } .entity_actions h2 { display:none; @@ -615,7 +616,7 @@ display:none; list-style-type:none; } .entity_actions li { -margin-bottom:4px; +margin-bottom:7px; } .entity_actions li:first-child { border-top:0; @@ -633,42 +634,97 @@ display:block; text-align:left; width:100%; } -.entity_actions a, -.entity_nudge p, -.entity_remote_subscribe { +.entity_actions a { text-decoration:none; font-weight:bold; display:block; } - -.form_user_block input.submit, -.form_user_unblock input.submit, -.entity_send-a-message a, -.entity_edit a, -.form_user_nudge input.submit, -.entity_nudge p { -border:0; -padding-left:20px; -} - -.entity_edit a, -.entity_send-a-message a, -.entity_nudge p { -padding:4px 4px 4px 23px; -} - -.entity_remote_subscribe { -padding:4px; -border-width:2px; -border-style:solid; +.entity_actions a, +.entity_actions input { border-radius:4px; -moz-border-radius:4px; -webkit-border-radius:4px; } + +.entity_actions a, +.entity_actions input, +.entity_actions p { +border-width:2px; +border-style:solid; +padding-left:23px; +} + +.entity_actions a, +.entity_actions p { +padding:2px 4px 1px 26px; +} + .entity_actions .accept { margin-bottom:18px; } +.entity_send-a-message button { +position:absolute; +top:3px; +right:3px; +} + +.entity_send-a-message .form_notice { +position:absolute; +top:34px; +right:-1px; +padding:1.795%; +width:65%; +z-index:2; + border-radius:7px; +-moz-border-radius:7px; +-webkit-border-radius:7px; +border-width:1px; +border-style:solid; +} +.entity_send-a-message .form_notice legend { +display:block; +margin-bottom:11px; +} + +.entity_send-a-message .form_notice label, +.entity_send-a-message .form_notice select { +display:none; +} +.entity_send-a-message .form_notice input.submit { +text-align:center; +} + +.entity_moderation { +position:relative; +} +.entity_moderation p { +border-radius:4px; +-moz-border-radius:4px; +-webkit-border-radius:4px; +font-weight:bold; +padding-bottom:2px; +margin-bottom:7px; +} +.entity_moderation ul { +display:none; +} +.entity_moderation:hover ul { +display:block; +min-width:21%; +width:100%; +padding:11px; +position:absolute; +top:-1px; +right:-1px; +z-index:1; +border-width:1px; +border-style:solid; +border-radius:7px; +-moz-border-radius:7px; +-webkit-border-radius:7px; +} + .entity_tags ul { list-style-type:none; display:inline; diff --git a/theme/biz/css/display.css b/theme/biz/css/display.css index 52f36ab54a..7fd78470f5 100644 --- a/theme/biz/css/display.css +++ b/theme/biz/css/display.css @@ -186,7 +186,9 @@ box-shadow:5px 7px 7px rgba(194, 194, 194, 0.3); border-color:#FFFFFF; } #content, -#site_nav_local_views .current a { +#site_nav_local_views .current a, +.entity_send-a-message .form_notice, +.entity_moderation:hover ul { background-color:#FFFFFF; } @@ -231,30 +233,22 @@ background-position:0 -64px; background-position:0 1px; } -.entity_edit a, -.entity_send-a-message a, -.form_user_nudge input.submit, -.form_user_block input.submit, -.form_user_unblock input.submit, -.form_group_block input.submit, -.form_group_unblock input.submit, -.entity_nudge p, -.form_make_admin input.submit { -background-position: 0 40%; -background-repeat: no-repeat; -background-color:transparent; -} .form_group_join input.submit, -.form_group_leave input.submit +.form_group_leave input.submit, .form_user_subscribe input.submit, -.form_user_unsubscribe input.submit { -background-color:#9BB43E; +.form_user_unsubscribe input.submit, +.entity_subscribe a { +background-color:#AAAAAA; color:#FFFFFF; } -.form_user_unsubscribe input.submit, .form_group_leave input.submit, -.form_user_authorization input.reject { -background-color:#87B4C8; +.form_user_unsubscribe input.submit { +background-position:5px -1246px; +} +.form_group_join input.submit, +.form_user_subscribe input.submit, +.entity_subscribe a { +background-position:5px -1181px; } .entity_edit a { @@ -263,6 +257,12 @@ background-position: 5px -718px; .entity_send-a-message a { background-position: 5px -852px; } +.entity_send-a-message .form_notice, +.entity_moderation:hover ul { +box-shadow:3px 7px 5px rgba(194, 194, 194, 0.7); +-moz-box-shadow:3px 7px 5px rgba(194, 194, 194, 0.7); +-webkit-box-shadow:3px 7px 5px rgba(194, 194, 194, 0.7); +} .entity_nudge p, .form_user_nudge input.submit { @@ -293,6 +293,7 @@ background-position: 5px -1511px; background-position: 5px -1973px; } + /* NOTICES */ .notice .attachment { background-position:0 -394px; From d29af38a0be2f0944297898b166e86d0cebd9bd2 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 28 Jan 2010 17:28:11 +0100 Subject: [PATCH 061/170] Update to biz theme's input styles --- theme/biz/css/display.css | 69 +++++++++++++++++++++++++++++++-------- 1 file changed, 55 insertions(+), 14 deletions(-) diff --git a/theme/biz/css/display.css b/theme/biz/css/display.css index 7fd78470f5..0b7c17de76 100644 --- a/theme/biz/css/display.css +++ b/theme/biz/css/display.css @@ -25,14 +25,33 @@ address { margin-right:7.18%; } +input, textarea, select { +border-width:2px; +border-style: solid; +border-radius:4px; +-moz-border-radius:4px; +-webkit-border-radius:4px; +} input, textarea, select, option { font-family: "Lucida Sans Unicode", "Lucida Grande", sans-serif; } -input, textarea, select, -.entity_remote_subscribe { +input, textarea, select { border-color:#AAAAAA; } -#filter_tags ul li { + +.form_settings fieldset fieldset { +background:rgba(240, 240, 240, 0.2); +box-shadow:3px 3px 7px rgba(194, 194, 194, 0.3); +-moz-box-shadow:3px 3px 7px rgba(194, 194, 194, 0.3); +-webkit-box-shadow:3px 3px 7px rgba(194, 194, 194, 0.3); +} + +#filter_tags ul li, +.entity_send-a-message .form_notice, +.pagination .nav_prev a, +.pagination .nav_next a, +.form_settings fieldset fieldset, +.entity_moderation:hover ul { border-color:#DDDDDD; } @@ -40,6 +59,34 @@ border-color:#DDDDDD; background:none; } +.form_notice.warning #notice_text-count, +.form_settings .form_note { +background-color:#9BB43E; +} +input.submit, +.form_notice.warning #notice_text-count, +.form_settings .form_note, +.entity_actions a, +.entity_actions input, +.entity_moderation p, +button { +box-shadow:3px 3px 3px rgba(194, 194, 194, 0.3); +-moz-box-shadow:3px 3px 3px rgba(194, 194, 194, 0.3); +-webkit-box-shadow:3px 3px 3px rgba(194, 194, 194, 0.3); +} +.entity_actions a, +.entity_actions input, +.entity_actions p { +border-color:transparent; +background-color:transparent; +} +input:focus, textarea:focus, select:focus, +.form_notice.warning #notice_data-text, +.form_notice.warning #notice_text-count, +.form_settings .form_note { +border-color:#9BB43E; +} + input.submit { color:#FFFFFF; } @@ -78,18 +125,12 @@ background-position:0 -1846px; } a, -#site_nav_local_views .current a, -div.notice-options input, -.form_user_block input.submit, -.form_user_unblock input.submit, -.form_group_block input.submit, -.form_group_unblock input.submit, -.entity_send-a-message a, -.form_user_nudge input.submit, -.entity_nudge p, .form_settings input.form_action-primary, -.form_make_admin input.submit { -color:#002E6E; +.notice-options input, +.entity_actions a, +.entity_actions input, +.entity_moderation p { +color:#002FA7; } #header a, From 1662aa11139517cccc600d65b9e8eba07c541ea7 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 28 Jan 2010 17:32:24 +0100 Subject: [PATCH 062/170] Update to biz theme button close and minimize styles --- theme/biz/css/base.css | 17 +++++++++++++++++ theme/biz/css/display.css | 7 +++++++ 2 files changed, 24 insertions(+) diff --git a/theme/biz/css/base.css b/theme/biz/css/base.css index bd70c083e2..8a34425be2 100644 --- a/theme/biz/css/base.css +++ b/theme/biz/css/base.css @@ -518,6 +518,11 @@ margin-bottom:0; line-height:1.618; } +.form_notice #notice_data-attach_selected button.close { +float:right; +font-size:0.8em; +} + .form_notice #notice_data-geo_wrap label, .form_notice #notice_data-geo_wrap input { position:absolute; @@ -539,6 +544,18 @@ margin-bottom:0; text-indent:-9999px; } +button.close, +button.minimize { +width:16px; +height:16px; +text-indent:-9999px; +padding:0; +border:0; +text-align:center; +font-weight:bold; +cursor:pointer; +} + /* entity_profile */ .entity_profile { position:relative; diff --git a/theme/biz/css/display.css b/theme/biz/css/display.css index 0b7c17de76..7768d5146a 100644 --- a/theme/biz/css/display.css +++ b/theme/biz/css/display.css @@ -256,6 +256,13 @@ background-color:#F7E8E8; background-color:#EFF3DC; } +button.close { +background-position:0 -1120px; +} +button.minimize { +background-position:0 -1912px; +} + #anon_notice { color:#FFFFFF; } From b4babedd29661a15a8dd135a20ffaf7059b7fa50 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 28 Jan 2010 17:36:33 +0100 Subject: [PATCH 063/170] Update to notice item in biz theme --- theme/biz/css/base.css | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/theme/biz/css/base.css b/theme/biz/css/base.css index 8a34425be2..366339db28 100644 --- a/theme/biz/css/base.css +++ b/theme/biz/css/base.css @@ -903,6 +903,16 @@ float:left; #shownotice .vcard .photo { margin-bottom:4px; } +#content .notice .author .photo { +position:absolute; +top:11px; +left:0; +float:none; +} +#content .notice .entry-title { +margin-left:59px; +} + .vcard .url { text-decoration:none; } @@ -911,12 +921,22 @@ text-decoration:underline; } .notice .entry-title { -float:left; -width:100%; overflow:hidden; } +.notice .entry-title.ov { +overflow:visible; +} +#showstream .notice .entry-title, +#showstream .notice div.entry-content { +margin-left:0; +} #shownotice .notice .entry-title { +margin-left:110px; font-size:2.2em; +min-height:123px; +} +#shownotice .notice div.entry-content { +margin-left:0; } .notice p.entry-content { @@ -939,7 +959,7 @@ clear:left; float:left; font-size:0.95em; margin-left:59px; -width:65%; +width:64%; } #showstream .notice div.entry-content, #shownotice .notice div.entry-content { From 22d9c32e7e755c36817912cb9e20554b23d211d4 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 28 Jan 2010 18:39:30 +0100 Subject: [PATCH 064/170] Update to aside styles in biz theme --- theme/biz/css/base.css | 2 +- theme/biz/css/display.css | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/theme/biz/css/base.css b/theme/biz/css/base.css index 366339db28..2c2ab33a01 100644 --- a/theme/biz/css/base.css +++ b/theme/biz/css/base.css @@ -422,7 +422,7 @@ float:left; width:29.917%; min-height:259px; float:left; -margin-left:0.385%; +margin-left:1%; } #form_notice { diff --git a/theme/biz/css/display.css b/theme/biz/css/display.css index 7768d5146a..f133ac30b9 100644 --- a/theme/biz/css/display.css +++ b/theme/biz/css/display.css @@ -138,10 +138,22 @@ color:#002FA7; color:#87B4C8; } + + .notice, -.profile { -border-top-color:#CEE1E9; +.profile, +.application, +#content tbody tr { +border-top-color:#C8D1D5; } +.mark-top { +border-color:#AAAAAA; +} + +#aside_primary { +background-color:#144A6E; +} + .section .profile { border-top-color:#87B4C8; } From a868a523a5ab042e75d333298a75aaa369d445cc Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Thu, 28 Jan 2010 09:52:35 -0800 Subject: [PATCH 065/170] Can now set $config['queue']['stomp_persistent'] = false; to explicitly disable persistence when we queue items --- lib/default.php | 1 + lib/stompqueuemanager.php | 9 +++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/default.php b/lib/default.php index c729193b52..8de8b10977 100644 --- a/lib/default.php +++ b/lib/default.php @@ -84,6 +84,7 @@ $default = 'control_channel' => '/topic/statusnet-control', // broadcasts to all queue daemons 'stomp_username' => null, 'stomp_password' => null, + 'stomp_persistent' => true, // keep items across queue server restart, if persistence is enabled 'monitor' => null, // URL to monitor ping endpoint (work in progress) 'softlimit' => '90%', // total size or % of memory_limit at which to restart queue threads gracefully 'debug_memory' => false, // true to spit memory usage to log diff --git a/lib/stompqueuemanager.php b/lib/stompqueuemanager.php index da70d9ae31..4e2b586025 100644 --- a/lib/stompqueuemanager.php +++ b/lib/stompqueuemanager.php @@ -174,12 +174,13 @@ class StompQueueManager extends QueueManager $this->_connect(); - // XXX: serialize and send entire notice - + $props = array('created' => common_sql_now()); + if (common_config('queue', 'stomp_persistent')) { + $props['persistent'] = 'true'; + } $result = $this->con->send($this->queueName($queue), $msg, // BODY of the message - array ('created' => common_sql_now(), - 'persistent' => 'true')); + $props); if (!$result) { common_log(LOG_ERR, "Error sending $rep to $queue queue"); From 558bf0f48907ad463872075c778c12bc5821f510 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Thu, 28 Jan 2010 18:11:44 +0000 Subject: [PATCH 066/170] 'Sign in with Twitter' button img --- .../Sign-in-with-Twitter-lighter.png | Bin 0 -> 2490 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 plugins/TwitterBridge/Sign-in-with-Twitter-lighter.png diff --git a/plugins/TwitterBridge/Sign-in-with-Twitter-lighter.png b/plugins/TwitterBridge/Sign-in-with-Twitter-lighter.png new file mode 100644 index 0000000000000000000000000000000000000000..297bb03404f2d7462ee9355aae38f5f5f3e47fbd GIT binary patch literal 2490 zcmV;r2}SmaP)dbVG7wVRUJ4ZXi@?ZDjy8FEKeU zFgbGL({lg-0338hSaefwW^{L9a%BKPWN%_+AVz6&Wp{6KYjYq&Q#1y$)1UwV2@^>~ zK~!jg?VEdWl+_u=f8X9WA)72QVM!vF1VSK4AZQ?nK}o}NseS3JN$f zgRQMIo!W~OJ6`ZouvAJ_K&eH%O=zU35Dd9+OGJ}EvYTu++57IdU;kJVc9XpY6B?a; zX3k{ZIp=-eC+|7m`<-)kR{+>vQ7sWi9D|5ebEgWz_KIq$s4!>9f*d_|j-lj_V|e1| zv2!Q@e7>PCTvsDT9K#C=07>!<2 zJRT3v{^4m(e0B`AMuXes=AQD^+;C$tPj22q>4H*zdfz>`#KAid3>9xAeo+*W%jE#P zv+pgwyLbU*^X6iX@D|vxdJPV{BV^y=rMIzo>FtA}CzL%H7|@3PC-uR0k{o`MEpT&+ zmiCUwd$DjaD&G5mR^R=5Ugy>A zFL6`pd;osE=`rd~)-ZEc5shchvh}x5aZ~9nJpS;;UjCggyhMFX4IM@k^;K2OnpeX9 zJ+G59W-M>Ndx+<@J`*QB2?^MN1N-(fead7?=HEi6T!B@Box5IR=L;`>Ej{PX zHuTf??pp_V_K#2V&%f=WuDXUP)2>6K(eP;bS}ruV@b7*5sr;ydIrB8I2tIsDgsYdEB~aA*X6k7? zxX|2;qp^|Gwe@`Y#d&IupCC)EqUw`l7)GU&uTKu*U$o?QG#U*z%qZYj8`hDRoCLu8 zhbzgOI5DbLyT^;&?d{E@=<{Loc(HlBWQ@7?YwIarxgyB6oH@guS9eiXI-mWozJlq( z1uEY@fC2%N(MZ*iqs%Lw&3$(-XHs%f#Ci8GF3A_N^~J`q@N(Yec4#`>p8;Rm>`^(w z^()mX);zY6A1}KTOQ(hBpZ+tI2M_vvC6EgOasg^TuSW9u`2L~=xMTvlF=IdirA&q( z01}jz+(`M#6-+Id=Jypvl2wZSngV!x>{xvBuA7QRt)c4kYU--0D3~!Fhr>aA^+`_G z)iHDWG@POs#IMsQgCL+*sYp&si#>KieJ zAZPaXZl!3>9M-SjNX7dfvh?=bdf)$&1YWO*1jqy#ZFVPakM~lt{ns24pi(Mnce*e+ zoV|U5)IWA0uWtapa=C)>xf3{EU4zZ(WX19y(%RC@hySVMd_x1@*}REPr@KGD1PCBr zszd|P3jULJU(P|*6JUEpf1ZUYMN@b5y{gO~R0`R^ zzo2^1b1iYS>vBugsL9qPM&yVTyCwWp(Uo=ev@y*W7rS{ZmOePbh#j}_&X%YtxeZa(=9L5<&(e8Bi z<5wsYm^&^0MKr*8(egMLdIIf9`S}2Jn9Y=xl~T570XoAdF4!DlHH6a}anEJ-cv*VQ z<;7KRgYq-}Q)5qi@;qcECS38JtLDV#Y+3g(n;(3LUp}&dB%O}ct5)I-xFV2%>k11g zTUf@C&yKTa*KVdw^(R50loR*{_b2SKCHeZt4jkLpj^;ZlKOe6sQZ!>aT9uM(b0*;N zdYL_=0IS=B(gj7|CTgIo3w=s5DLNgUE;lDl z=6*ck(9~r1FRqNt5);TzP2p%4ylH~wfExv!+`pk8s}?!j(i+mgrP)GJVGd2L zrifxR3OQ=I9MLCX4Ray;#3LJ#2{HtkfT^vG#S0hk#JUIhwAC2&WDQrS)2nfk*V=iQ!Y2K(sVlqHpM%x^1{ck;y4Kin5jc`W;@8<8AFx4+YWs z?QSo2A}4+Q<6F?#y0F{r Date: Thu, 28 Jan 2010 18:34:25 +0000 Subject: [PATCH 067/170] Remove redundant session token field from form (was already being added by base class). --- lib/applicationeditform.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/applicationeditform.php b/lib/applicationeditform.php index 6f03a9beda..9b7d058614 100644 --- a/lib/applicationeditform.php +++ b/lib/applicationeditform.php @@ -168,8 +168,6 @@ class ApplicationEditForm extends Form $this->access_type = ''; } - $this->out->hidden('token', common_session_token()); - $this->out->elementStart('ul', 'form_data'); $this->out->elementStart('li', array('id' => 'application_icon')); From 612dce4fe12e20643c2d33910c2fdf361a4845a5 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 28 Jan 2010 16:39:20 +0100 Subject: [PATCH 068/170] Fixed layout for powered by statusnet in biz --- theme/biz/css/base.css | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/theme/biz/css/base.css b/theme/biz/css/base.css index 6357e55b4e..471ac580d9 100644 --- a/theme/biz/css/base.css +++ b/theme/biz/css/base.css @@ -224,6 +224,15 @@ font-weight:bold; address img + .fn { display:none; } +address .poweredby { +float:left; +clear:left; +display:block; +position:relative; +top:7px; +margin-right:-47px; +} + #header { width:100%; From df2390a90f43e7493eef492960526df112efa9a4 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 28 Jan 2010 16:41:28 +0100 Subject: [PATCH 069/170] Fixed layout when ad plugin is on for biz --- theme/biz/css/base.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/theme/biz/css/base.css b/theme/biz/css/base.css index 471ac580d9..2d4ac85ba6 100644 --- a/theme/biz/css/base.css +++ b/theme/biz/css/base.css @@ -396,7 +396,7 @@ margin-bottom:1em; } #content { -width:51.009%; +width:50%; min-height:259px; padding:1.795%; float:left; From 108aa050af17639c319305338ea9de9e28847275 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 28 Jan 2010 16:49:33 +0100 Subject: [PATCH 070/170] Updated geo sharing styles for biz --- theme/biz/css/base.css | 21 +++++++++++++++++++++ theme/biz/css/display.css | 15 ++++++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/theme/biz/css/base.css b/theme/biz/css/base.css index 2d4ac85ba6..47845421a3 100644 --- a/theme/biz/css/base.css +++ b/theme/biz/css/base.css @@ -518,6 +518,27 @@ margin-bottom:0; line-height:1.618; } +.form_notice #notice_data-geo_wrap label, +.form_notice #notice_data-geo_wrap input { +position:absolute; +top:25px; +right:4px; +left:auto; +cursor:pointer; +width:16px; +height:16px; +display:block; +} +.form_notice #notice_data-geo_wrap input { +visibility:hidden; +} +.form_notice #notice_data-geo_wrap label { +font-weight:normal; +font-size:1em; +margin-bottom:0; +text-indent:-9999px; +} + /* entity_profile */ .entity_profile { position:relative; diff --git a/theme/biz/css/display.css b/theme/biz/css/display.css index 7ea4515769..7a53b02bfc 100644 --- a/theme/biz/css/display.css +++ b/theme/biz/css/display.css @@ -60,6 +60,13 @@ input.submit, color:#FFFFFF; } +.form_notice label[for=notice_data-geo] { +background-position:0 -1780px; +} +.form_notice label[for=notice_data-geo].checked { +background-position:0 -1846px; +} + a, #site_nav_local_views .current a, div.notice-options input, @@ -115,6 +122,12 @@ text-indent:-9999px; outline:none; } +.form_notice label[for=notice_data-geo] { +background-image:url(../../base/images/icons/icons-01.gif); +background-repeat:no-repeat; +background-color:transparent; +} + #content { box-shadow:5px 7px 7px rgba(194, 194, 194, 0.3); -moz-box-shadow:5px 7px 7px rgba(194, 194, 194, 0.3); @@ -130,7 +143,7 @@ border-color:#FFFFFF; background-color:#FFFFFF; } -#site_nav_local_views li { +#site_nav_local_views li.current { box-shadow:3px 7px 5px rgba(194, 194, 194, 0.5); -moz-box-shadow:3px 7px 5px rgba(194, 194, 194, 0.5); -webkit-box-shadow:3px 7px 5px rgba(194, 194, 194, 0.5); From f4c037f956ac7ca638c3d800f2d9070aa31d45e0 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 28 Jan 2010 17:08:24 +0100 Subject: [PATCH 071/170] Updated biz theme to use the single icons file --- theme/biz/css/base.css | 20 +++--- theme/biz/css/display.css | 132 +++++++++++++++++++++++++++++--------- 2 files changed, 110 insertions(+), 42 deletions(-) diff --git a/theme/biz/css/base.css b/theme/biz/css/base.css index 47845421a3..4ce7b49caa 100644 --- a/theme/biz/css/base.css +++ b/theme/biz/css/base.css @@ -911,25 +911,21 @@ margin-right:11px; .notice-options a { float:left; } -.notice-options .notice_delete, .notice-options .notice_reply, +.notice-options .form_repeat, .notice-options .form_favor, -.notice-options .form_disfavor { -position:absolute; -top:0; +.notice-options .form_disfavor, +.notice-options .repeated { +float:left; +margin-left:14.2%; } .notice-options .form_favor, .notice-options .form_disfavor { -left:0; -} -.notice-options .notice_reply { -left:29px; -} -.notice-options .notice_delete { -right:0; +margin-left:0; } .notice-options input, -.notice-options a { +.notice-options a, +.notice-options .repeated { text-indent:-9999px; outline:none; } diff --git a/theme/biz/css/display.css b/theme/biz/css/display.css index 7a53b02bfc..4dfd25a996 100644 --- a/theme/biz/css/display.css +++ b/theme/biz/css/display.css @@ -108,26 +108,63 @@ color:#333333; #form_notice.warning #notice_text-count { color:#000000; } -#form_notice label[for=notice_data-attach] { -background:transparent url(../../base/images/icons/twotone/green/clip-01.gif) no-repeat 0 45%; +.form_notice label[for=notice_data-attach] { +background-position:0 -328px; } -#form_notice #notice_data-attach { +.form_notice #notice_data-attach { opacity:0; } -#wrap form.processing input.submit { -background:#FFFFFF url(../../base/images/icons/icon_processing.gif) no-repeat 47% 47%; -cursor:wait; -text-indent:-9999px; -outline:none; -} - -.form_notice label[for=notice_data-geo] { +.form_notice label[for=notice_data-attach], +#export_data li a.rss, +#export_data li a.atom, +#export_data li a.foaf, +.entity_edit a, +.entity_send-a-message a, +.entity_nudge p, +.form_user_nudge input.submit, +.form_user_block input.submit, +.form_user_unblock input.submit, +.form_group_block input.submit, +.form_group_unblock input.submit, +.form_make_admin input.submit, +.notice .attachment, +.notice-options .notice_reply, +.notice-options form.form_favor input.submit, +.notice-options form.form_disfavor input.submit, +.notice-options .notice_delete, +.notice-options form.form_repeat input.submit, +#new_group a, +.pagination .nav_prev a, +.pagination .nav_next a, +button.close, +.form_group_leave input.submit, +.form_user_unsubscribe input.submit, +.form_group_join input.submit, +.form_user_subscribe input.submit, +.entity_subscribe a, +.entity_moderation p, +.entity_sandbox input.submit, +.entity_silence input.submit, +.entity_delete input.submit, +.notice-options .repeated, +.form_notice label[for=notice_data-geo], +button.minimize, +.form_reset_key input.submit { background-image:url(../../base/images/icons/icons-01.gif); background-repeat:no-repeat; background-color:transparent; } +#wrap form.processing input.submit, +.entity_actions a.processing, +.dialogbox.processing .submit_dialogbox { +background:#FFFFFF url(../../base/images/icons/icon_processing.gif) no-repeat 47% 47%; +} +.notice-options .form_repeat.processing { +background-image:none; +} + #content { box-shadow:5px 7px 7px rgba(194, 194, 194, 0.3); -moz-box-shadow:5px 7px 7px rgba(194, 194, 194, 0.3); @@ -175,13 +212,13 @@ background-repeat:no-repeat; background-position:0 45%; } #export_data li a.rss { -background-image:url(../../base/images/icons/icon_rss.png); +background-position:0 -130px; } #export_data li a.atom { -background-image:url(../../base/images/icons/icon_atom.png); +background-position:0 -64px; } #export_data li a.foaf { -background-image:url(../../base/images/icons/icon_foaf.gif); +background-position:0 1px; } .entity_edit a, @@ -211,43 +248,65 @@ background-color:#87B4C8; } .entity_edit a { -background-image:url(../../base/images/icons/twotone/green/edit.gif); +background-position: 5px -718px; } .entity_send-a-message a { -background-image:url(../../base/images/icons/twotone/green/quote.gif); +background-position: 5px -852px; } + .entity_nudge p, .form_user_nudge input.submit { -background-image:url(../../base/images/icons/twotone/green/mail.gif); +background-position: 5px -785px; } .form_user_block input.submit, .form_user_unblock input.submit, .form_group_block input.submit, .form_group_unblock input.submit { -background-image:url(../../base/images/icons/twotone/green/shield.gif); +background-position: 5px -918px; } .form_make_admin input.submit { -background-image:url(../../base/images/icons/twotone/green/admin.gif); +background-position: 5px -983px; +} +.entity_moderation p { +background-position: 5px -1313px; +} +.entity_sandbox input.submit { +background-position: 5px -1380px; +} +.entity_silence input.submit { +background-position: 5px -1445px; +} +.entity_delete input.submit { +background-position: 5px -1511px; +} +.form_reset_key input.submit { +background-position: 5px -1973px; } /* NOTICES */ .notice .attachment { -background:transparent url(../../base/images/icons/twotone/green/clip-02.gif) no-repeat 0 45%; +background-position:0 -394px; } #attachments .attachment { background:none; } .notice-options .notice_reply { -background:transparent url(../../base/images/icons/twotone/green/reply.gif) no-repeat 0 45%; +background-position:0 -592px; } .notice-options form.form_favor input.submit { -background:transparent url(../../base/images/icons/twotone/green/favourite.gif) no-repeat 0 45%; +background-position:0 -460px; } .notice-options form.form_disfavor input.submit { -background:transparent url(../../base/images/icons/twotone/green/disfavourite.gif) no-repeat 0 45%; +background-position:0 -526px; } .notice-options .notice_delete { -background:transparent url(../../base/images/icons/twotone/green/trash.gif) no-repeat 0 45%; +background-position:0 -658px; +} +.notice-options form.form_repeat input.submit { +background-position:0 -1582px; +} +.notice-options .repeated { +background-position:0 -1648px; } .notices div.entry-content, @@ -284,19 +343,32 @@ background-color:rgba(200, 200, 200, 0.300); /*END: NOTICES */ #new_group a { -background:transparent url(../../base/images/icons/twotone/green/news.gif) no-repeat 0 45%; +background-position:0 -1054px; } .pagination .nav_prev a, .pagination .nav_next a { background-repeat:no-repeat; -border-color:#CEE1E9; +box-shadow:3px 3px 3px rgba(194, 194, 194, 0.3); +-moz-box-shadow:3px 3px 3px rgba(194, 194, 194, 0.3); +-webkit-box-shadow:3px 3px 3px rgba(194, 194, 194, 0.3); } .pagination .nav_prev a { -background-image:url(../../base/images/icons/twotone/green/arrow-left.gif); -background-position:10% 45%; +background-position:10% -187px; } .pagination .nav_next a { -background-image:url(../../base/images/icons/twotone/green/arrow-right.gif); -background-position:90% 45%; +background-position:105% -252px; +} +.pagination .nav .processing { +background-image:url(../../base/images/icons/icon_processing.gif); +box-shadow:none; +-moz-box-shadow:none; +-webkit-box-shadow:none; +outline:none; +} +.pagination .nav_next a.processing { +background-position:90% 47%; +} +.pagination .nav_prev a.processing { +background-position:10% 47%; } From 8a18fd9591cfc8772d58b14f8a532558e70911f6 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 28 Jan 2010 17:09:35 +0100 Subject: [PATCH 072/170] Updated biz theme to hide form_repeat legend --- theme/biz/css/base.css | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/theme/biz/css/base.css b/theme/biz/css/base.css index 4ce7b49caa..ec8ca22f5f 100644 --- a/theme/biz/css/base.css +++ b/theme/biz/css/base.css @@ -942,17 +942,18 @@ padding-left:16px; width:16px; padding:2px 0; } +.notice-options .form_repeat legend, .notice-options .form_favor legend, .notice-options .form_disfavor legend { display:none; } +.notice-options .form_repeat fieldset, .notice-options .form_favor fieldset, .notice-options .form_disfavor fieldset { border:0; padding:0; } - #usergroups #new_group { float: left; margin-right: 2em; From 852a8b8295066140d4745b8a92eb835e8be6c3b0 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 28 Jan 2010 17:12:48 +0100 Subject: [PATCH 073/170] Updated biz theme notice options --- theme/biz/css/base.css | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/theme/biz/css/base.css b/theme/biz/css/base.css index ec8ca22f5f..d5873b0b07 100644 --- a/theme/biz/css/base.css +++ b/theme/biz/css/base.css @@ -903,9 +903,10 @@ text-transform:lowercase; .notice-options { position:relative; font-size:0.95em; -width:90px; +width:113px; float:right; -margin-right:11px; +margin-top:3px; +margin-right:4px; } .notice-options a { @@ -936,11 +937,17 @@ border:0; .notice-options .notice_reply, .notice-options .notice_delete { text-decoration:none; -padding-left:16px; +} +.notice .notice-options .notice_delete { +float:right; } .notice-options form input.submit { width:16px; -padding:2px 0; +height:16px; +padding:0; +border-radius:0; +-moz-border-radius:0; +-webkit-border-radius:0; } .notice-options .form_repeat legend, .notice-options .form_favor legend, @@ -953,6 +960,11 @@ display:none; border:0; padding:0; } +.notice-options a, +.notice-options .repeated { +width:16px; +height:16px; +} #usergroups #new_group { float: left; From 9af751c03a08445b7e1fb79af60a60c65a81f89f Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 28 Jan 2010 17:16:11 +0100 Subject: [PATCH 074/170] Updated biz theme to use dialogbox styles --- theme/biz/css/base.css | 43 ++++++++++++++++++++++++++++++++++++++ theme/biz/css/display.css | 44 ++++++++++++++++++++++++--------------- 2 files changed, 70 insertions(+), 17 deletions(-) diff --git a/theme/biz/css/base.css b/theme/biz/css/base.css index d5873b0b07..a8834ca570 100644 --- a/theme/biz/css/base.css +++ b/theme/biz/css/base.css @@ -900,6 +900,49 @@ display:inline-block; text-transform:lowercase; } +.dialogbox { +position:absolute; +top:-4px; +right:29px; +z-index:9; +min-width:199px; +float:none; +background-color:#FFF; +padding:11px; +border-radius:7px; +-moz-border-radius:7px; +-webkit-border-radius:7px; +border-style:solid; +border-width:1px; +border-color:#DDDDDD; +-moz-box-shadow:3px 7px 5px rgba(194, 194, 194, 0.7); +} + +.dialogbox legend { +display:block !important; +margin-right:18px; +} + +.dialogbox button.close { +position:absolute; +right:3px; +top:3px; +} + +.dialogbox .submit_dialogbox { +font-weight:bold; +text-indent:0; +min-width:46px; +} + +#wrap form.processing input.submit, +.entity_actions a.processing, +.dialogbox.processing .submit_dialogbox { +cursor:wait; +outline:none; +text-indent:-9999px; +} + .notice-options { position:relative; font-size:0.95em; diff --git a/theme/biz/css/display.css b/theme/biz/css/display.css index 4dfd25a996..52f36ab54a 100644 --- a/theme/biz/css/display.css +++ b/theme/biz/css/display.css @@ -40,25 +40,35 @@ border-color:#DDDDDD; background:none; } -input.submit, -#form_notice.warning #notice_text-count, -.form_settings .form_note, -.entity_remote_subscribe { -background-color:#9BB43E; -} - -input:focus, textarea:focus, select:focus, -#form_notice.warning #notice_data-text { -border-color:#9BB43E; -box-shadow:3px 3px 3px rgba(194, 194, 194, 0.3); --moz-box-shadow:3px 3px 3px rgba(194, 194, 194, 0.3); --webkit-box-shadow:3px 3px 3px rgba(194, 194, 194, 0.3); -} -input.submit, -.entity_remote_subscribe, -#site_nav_local_views a { +input.submit { color:#FFFFFF; } +.entity_actions input.submit { +border-color:transparent; +text-shadow:none; +} +.dialogbox .submit_dialogbox, +input.submit, +.form_notice input.submit { +background:#AAAAAA url(../../base/images/illustrations/illu_pattern-01.png) 0 0 repeat-x; +text-shadow:0 1px 0 #FFFFFF; +color:#000000; +border-color:#AAAAAA; +border-top-color:#CCCCCC; +border-left-color:#CCCCCC; +} +.dialogbox .submit_dialogbox:hover, +input.submit:hover { +background-position:0 -5px; +} +.dialogbox .submit_dialogbox:focus, +input.submit:focus { +background-position:0 -15px; +box-shadow:3px 3px 3px rgba(194, 194, 194, 0.1); +-moz-box-shadow:3px 3px 3px rgba(194, 194, 194, 0.1); +-webkit-box-shadow:3px 3px 3px rgba(194, 194, 194, 0.1); +text-shadow:none; +} .form_notice label[for=notice_data-geo] { background-position:0 -1780px; From f66e0ed1392a01e925e3d5dcc1772d32cad6c271 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 28 Jan 2010 17:22:01 +0100 Subject: [PATCH 075/170] Updated biz theme entity_actions styles --- theme/biz/css/base.css | 110 ++++++++++++++++++++++++++++---------- theme/biz/css/display.css | 41 +++++++------- 2 files changed, 104 insertions(+), 47 deletions(-) diff --git a/theme/biz/css/base.css b/theme/biz/css/base.css index a8834ca570..bd70c083e2 100644 --- a/theme/biz/css/base.css +++ b/theme/biz/css/base.css @@ -605,8 +605,9 @@ display:none; /*entity_actions*/ .entity_actions { float:right; -margin-left:4.35%; -max-width:25%; +margin-left:2%; +margin-bottom:18px; +min-width:21%; } .entity_actions h2 { display:none; @@ -615,7 +616,7 @@ display:none; list-style-type:none; } .entity_actions li { -margin-bottom:4px; +margin-bottom:7px; } .entity_actions li:first-child { border-top:0; @@ -633,42 +634,97 @@ display:block; text-align:left; width:100%; } -.entity_actions a, -.entity_nudge p, -.entity_remote_subscribe { +.entity_actions a { text-decoration:none; font-weight:bold; display:block; } - -.form_user_block input.submit, -.form_user_unblock input.submit, -.entity_send-a-message a, -.entity_edit a, -.form_user_nudge input.submit, -.entity_nudge p { -border:0; -padding-left:20px; -} - -.entity_edit a, -.entity_send-a-message a, -.entity_nudge p { -padding:4px 4px 4px 23px; -} - -.entity_remote_subscribe { -padding:4px; -border-width:2px; -border-style:solid; +.entity_actions a, +.entity_actions input { border-radius:4px; -moz-border-radius:4px; -webkit-border-radius:4px; } + +.entity_actions a, +.entity_actions input, +.entity_actions p { +border-width:2px; +border-style:solid; +padding-left:23px; +} + +.entity_actions a, +.entity_actions p { +padding:2px 4px 1px 26px; +} + .entity_actions .accept { margin-bottom:18px; } +.entity_send-a-message button { +position:absolute; +top:3px; +right:3px; +} + +.entity_send-a-message .form_notice { +position:absolute; +top:34px; +right:-1px; +padding:1.795%; +width:65%; +z-index:2; + border-radius:7px; +-moz-border-radius:7px; +-webkit-border-radius:7px; +border-width:1px; +border-style:solid; +} +.entity_send-a-message .form_notice legend { +display:block; +margin-bottom:11px; +} + +.entity_send-a-message .form_notice label, +.entity_send-a-message .form_notice select { +display:none; +} +.entity_send-a-message .form_notice input.submit { +text-align:center; +} + +.entity_moderation { +position:relative; +} +.entity_moderation p { +border-radius:4px; +-moz-border-radius:4px; +-webkit-border-radius:4px; +font-weight:bold; +padding-bottom:2px; +margin-bottom:7px; +} +.entity_moderation ul { +display:none; +} +.entity_moderation:hover ul { +display:block; +min-width:21%; +width:100%; +padding:11px; +position:absolute; +top:-1px; +right:-1px; +z-index:1; +border-width:1px; +border-style:solid; +border-radius:7px; +-moz-border-radius:7px; +-webkit-border-radius:7px; +} + .entity_tags ul { list-style-type:none; display:inline; diff --git a/theme/biz/css/display.css b/theme/biz/css/display.css index 52f36ab54a..7fd78470f5 100644 --- a/theme/biz/css/display.css +++ b/theme/biz/css/display.css @@ -186,7 +186,9 @@ box-shadow:5px 7px 7px rgba(194, 194, 194, 0.3); border-color:#FFFFFF; } #content, -#site_nav_local_views .current a { +#site_nav_local_views .current a, +.entity_send-a-message .form_notice, +.entity_moderation:hover ul { background-color:#FFFFFF; } @@ -231,30 +233,22 @@ background-position:0 -64px; background-position:0 1px; } -.entity_edit a, -.entity_send-a-message a, -.form_user_nudge input.submit, -.form_user_block input.submit, -.form_user_unblock input.submit, -.form_group_block input.submit, -.form_group_unblock input.submit, -.entity_nudge p, -.form_make_admin input.submit { -background-position: 0 40%; -background-repeat: no-repeat; -background-color:transparent; -} .form_group_join input.submit, -.form_group_leave input.submit +.form_group_leave input.submit, .form_user_subscribe input.submit, -.form_user_unsubscribe input.submit { -background-color:#9BB43E; +.form_user_unsubscribe input.submit, +.entity_subscribe a { +background-color:#AAAAAA; color:#FFFFFF; } -.form_user_unsubscribe input.submit, .form_group_leave input.submit, -.form_user_authorization input.reject { -background-color:#87B4C8; +.form_user_unsubscribe input.submit { +background-position:5px -1246px; +} +.form_group_join input.submit, +.form_user_subscribe input.submit, +.entity_subscribe a { +background-position:5px -1181px; } .entity_edit a { @@ -263,6 +257,12 @@ background-position: 5px -718px; .entity_send-a-message a { background-position: 5px -852px; } +.entity_send-a-message .form_notice, +.entity_moderation:hover ul { +box-shadow:3px 7px 5px rgba(194, 194, 194, 0.7); +-moz-box-shadow:3px 7px 5px rgba(194, 194, 194, 0.7); +-webkit-box-shadow:3px 7px 5px rgba(194, 194, 194, 0.7); +} .entity_nudge p, .form_user_nudge input.submit { @@ -293,6 +293,7 @@ background-position: 5px -1511px; background-position: 5px -1973px; } + /* NOTICES */ .notice .attachment { background-position:0 -394px; From 72fc0f6b8a4ad392142c64f075dd161a50e72c7c Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 28 Jan 2010 17:28:11 +0100 Subject: [PATCH 076/170] Update to biz theme's input styles --- theme/biz/css/display.css | 69 +++++++++++++++++++++++++++++++-------- 1 file changed, 55 insertions(+), 14 deletions(-) diff --git a/theme/biz/css/display.css b/theme/biz/css/display.css index 7fd78470f5..0b7c17de76 100644 --- a/theme/biz/css/display.css +++ b/theme/biz/css/display.css @@ -25,14 +25,33 @@ address { margin-right:7.18%; } +input, textarea, select { +border-width:2px; +border-style: solid; +border-radius:4px; +-moz-border-radius:4px; +-webkit-border-radius:4px; +} input, textarea, select, option { font-family: "Lucida Sans Unicode", "Lucida Grande", sans-serif; } -input, textarea, select, -.entity_remote_subscribe { +input, textarea, select { border-color:#AAAAAA; } -#filter_tags ul li { + +.form_settings fieldset fieldset { +background:rgba(240, 240, 240, 0.2); +box-shadow:3px 3px 7px rgba(194, 194, 194, 0.3); +-moz-box-shadow:3px 3px 7px rgba(194, 194, 194, 0.3); +-webkit-box-shadow:3px 3px 7px rgba(194, 194, 194, 0.3); +} + +#filter_tags ul li, +.entity_send-a-message .form_notice, +.pagination .nav_prev a, +.pagination .nav_next a, +.form_settings fieldset fieldset, +.entity_moderation:hover ul { border-color:#DDDDDD; } @@ -40,6 +59,34 @@ border-color:#DDDDDD; background:none; } +.form_notice.warning #notice_text-count, +.form_settings .form_note { +background-color:#9BB43E; +} +input.submit, +.form_notice.warning #notice_text-count, +.form_settings .form_note, +.entity_actions a, +.entity_actions input, +.entity_moderation p, +button { +box-shadow:3px 3px 3px rgba(194, 194, 194, 0.3); +-moz-box-shadow:3px 3px 3px rgba(194, 194, 194, 0.3); +-webkit-box-shadow:3px 3px 3px rgba(194, 194, 194, 0.3); +} +.entity_actions a, +.entity_actions input, +.entity_actions p { +border-color:transparent; +background-color:transparent; +} +input:focus, textarea:focus, select:focus, +.form_notice.warning #notice_data-text, +.form_notice.warning #notice_text-count, +.form_settings .form_note { +border-color:#9BB43E; +} + input.submit { color:#FFFFFF; } @@ -78,18 +125,12 @@ background-position:0 -1846px; } a, -#site_nav_local_views .current a, -div.notice-options input, -.form_user_block input.submit, -.form_user_unblock input.submit, -.form_group_block input.submit, -.form_group_unblock input.submit, -.entity_send-a-message a, -.form_user_nudge input.submit, -.entity_nudge p, .form_settings input.form_action-primary, -.form_make_admin input.submit { -color:#002E6E; +.notice-options input, +.entity_actions a, +.entity_actions input, +.entity_moderation p { +color:#002FA7; } #header a, From 8eec008b0ce96408d295745fe1c0ea66f876de34 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 28 Jan 2010 17:32:24 +0100 Subject: [PATCH 077/170] Update to biz theme button close and minimize styles --- theme/biz/css/base.css | 17 +++++++++++++++++ theme/biz/css/display.css | 7 +++++++ 2 files changed, 24 insertions(+) diff --git a/theme/biz/css/base.css b/theme/biz/css/base.css index bd70c083e2..8a34425be2 100644 --- a/theme/biz/css/base.css +++ b/theme/biz/css/base.css @@ -518,6 +518,11 @@ margin-bottom:0; line-height:1.618; } +.form_notice #notice_data-attach_selected button.close { +float:right; +font-size:0.8em; +} + .form_notice #notice_data-geo_wrap label, .form_notice #notice_data-geo_wrap input { position:absolute; @@ -539,6 +544,18 @@ margin-bottom:0; text-indent:-9999px; } +button.close, +button.minimize { +width:16px; +height:16px; +text-indent:-9999px; +padding:0; +border:0; +text-align:center; +font-weight:bold; +cursor:pointer; +} + /* entity_profile */ .entity_profile { position:relative; diff --git a/theme/biz/css/display.css b/theme/biz/css/display.css index 0b7c17de76..7768d5146a 100644 --- a/theme/biz/css/display.css +++ b/theme/biz/css/display.css @@ -256,6 +256,13 @@ background-color:#F7E8E8; background-color:#EFF3DC; } +button.close { +background-position:0 -1120px; +} +button.minimize { +background-position:0 -1912px; +} + #anon_notice { color:#FFFFFF; } From 1e8b14d24b8ef748b4451322b7ff68aaa1f00eae Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 28 Jan 2010 17:36:33 +0100 Subject: [PATCH 078/170] Update to notice item in biz theme --- theme/biz/css/base.css | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/theme/biz/css/base.css b/theme/biz/css/base.css index 8a34425be2..366339db28 100644 --- a/theme/biz/css/base.css +++ b/theme/biz/css/base.css @@ -903,6 +903,16 @@ float:left; #shownotice .vcard .photo { margin-bottom:4px; } +#content .notice .author .photo { +position:absolute; +top:11px; +left:0; +float:none; +} +#content .notice .entry-title { +margin-left:59px; +} + .vcard .url { text-decoration:none; } @@ -911,12 +921,22 @@ text-decoration:underline; } .notice .entry-title { -float:left; -width:100%; overflow:hidden; } +.notice .entry-title.ov { +overflow:visible; +} +#showstream .notice .entry-title, +#showstream .notice div.entry-content { +margin-left:0; +} #shownotice .notice .entry-title { +margin-left:110px; font-size:2.2em; +min-height:123px; +} +#shownotice .notice div.entry-content { +margin-left:0; } .notice p.entry-content { @@ -939,7 +959,7 @@ clear:left; float:left; font-size:0.95em; margin-left:59px; -width:65%; +width:64%; } #showstream .notice div.entry-content, #shownotice .notice div.entry-content { From 171c97f17eecb3165d6ac088fece15a56f7c9914 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 28 Jan 2010 18:39:30 +0100 Subject: [PATCH 079/170] Update to aside styles in biz theme --- theme/biz/css/base.css | 2 +- theme/biz/css/display.css | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/theme/biz/css/base.css b/theme/biz/css/base.css index 366339db28..2c2ab33a01 100644 --- a/theme/biz/css/base.css +++ b/theme/biz/css/base.css @@ -422,7 +422,7 @@ float:left; width:29.917%; min-height:259px; float:left; -margin-left:0.385%; +margin-left:1%; } #form_notice { diff --git a/theme/biz/css/display.css b/theme/biz/css/display.css index 7768d5146a..f133ac30b9 100644 --- a/theme/biz/css/display.css +++ b/theme/biz/css/display.css @@ -138,10 +138,22 @@ color:#002FA7; color:#87B4C8; } + + .notice, -.profile { -border-top-color:#CEE1E9; +.profile, +.application, +#content tbody tr { +border-top-color:#C8D1D5; } +.mark-top { +border-color:#AAAAAA; +} + +#aside_primary { +background-color:#144A6E; +} + .section .profile { border-top-color:#87B4C8; } From e5ff610e755e205f06dbe5ada20fcfac2f2bb669 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Thu, 28 Jan 2010 18:11:44 +0000 Subject: [PATCH 080/170] 'Sign in with Twitter' button img --- .../Sign-in-with-Twitter-lighter.png | Bin 0 -> 2490 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 plugins/TwitterBridge/Sign-in-with-Twitter-lighter.png diff --git a/plugins/TwitterBridge/Sign-in-with-Twitter-lighter.png b/plugins/TwitterBridge/Sign-in-with-Twitter-lighter.png new file mode 100644 index 0000000000000000000000000000000000000000..297bb03404f2d7462ee9355aae38f5f5f3e47fbd GIT binary patch literal 2490 zcmV;r2}SmaP)dbVG7wVRUJ4ZXi@?ZDjy8FEKeU zFgbGL({lg-0338hSaefwW^{L9a%BKPWN%_+AVz6&Wp{6KYjYq&Q#1y$)1UwV2@^>~ zK~!jg?VEdWl+_u=f8X9WA)72QVM!vF1VSK4AZQ?nK}o}NseS3JN$f zgRQMIo!W~OJ6`ZouvAJ_K&eH%O=zU35Dd9+OGJ}EvYTu++57IdU;kJVc9XpY6B?a; zX3k{ZIp=-eC+|7m`<-)kR{+>vQ7sWi9D|5ebEgWz_KIq$s4!>9f*d_|j-lj_V|e1| zv2!Q@e7>PCTvsDT9K#C=07>!<2 zJRT3v{^4m(e0B`AMuXes=AQD^+;C$tPj22q>4H*zdfz>`#KAid3>9xAeo+*W%jE#P zv+pgwyLbU*^X6iX@D|vxdJPV{BV^y=rMIzo>FtA}CzL%H7|@3PC-uR0k{o`MEpT&+ zmiCUwd$DjaD&G5mR^R=5Ugy>A zFL6`pd;osE=`rd~)-ZEc5shchvh}x5aZ~9nJpS;;UjCggyhMFX4IM@k^;K2OnpeX9 zJ+G59W-M>Ndx+<@J`*QB2?^MN1N-(fead7?=HEi6T!B@Box5IR=L;`>Ej{PX zHuTf??pp_V_K#2V&%f=WuDXUP)2>6K(eP;bS}ruV@b7*5sr;ydIrB8I2tIsDgsYdEB~aA*X6k7? zxX|2;qp^|Gwe@`Y#d&IupCC)EqUw`l7)GU&uTKu*U$o?QG#U*z%qZYj8`hDRoCLu8 zhbzgOI5DbLyT^;&?d{E@=<{Loc(HlBWQ@7?YwIarxgyB6oH@guS9eiXI-mWozJlq( z1uEY@fC2%N(MZ*iqs%Lw&3$(-XHs%f#Ci8GF3A_N^~J`q@N(Yec4#`>p8;Rm>`^(w z^()mX);zY6A1}KTOQ(hBpZ+tI2M_vvC6EgOasg^TuSW9u`2L~=xMTvlF=IdirA&q( z01}jz+(`M#6-+Id=Jypvl2wZSngV!x>{xvBuA7QRt)c4kYU--0D3~!Fhr>aA^+`_G z)iHDWG@POs#IMsQgCL+*sYp&si#>KieJ zAZPaXZl!3>9M-SjNX7dfvh?=bdf)$&1YWO*1jqy#ZFVPakM~lt{ns24pi(Mnce*e+ zoV|U5)IWA0uWtapa=C)>xf3{EU4zZ(WX19y(%RC@hySVMd_x1@*}REPr@KGD1PCBr zszd|P3jULJU(P|*6JUEpf1ZUYMN@b5y{gO~R0`R^ zzo2^1b1iYS>vBugsL9qPM&yVTyCwWp(Uo=ev@y*W7rS{ZmOePbh#j}_&X%YtxeZa(=9L5<&(e8Bi z<5wsYm^&^0MKr*8(egMLdIIf9`S}2Jn9Y=xl~T570XoAdF4!DlHH6a}anEJ-cv*VQ z<;7KRgYq-}Q)5qi@;qcECS38JtLDV#Y+3g(n;(3LUp}&dB%O}ct5)I-xFV2%>k11g zTUf@C&yKTa*KVdw^(R50loR*{_b2SKCHeZt4jkLpj^;ZlKOe6sQZ!>aT9uM(b0*;N zdYL_=0IS=B(gj7|CTgIo3w=s5DLNgUE;lDl z=6*ck(9~r1FRqNt5);TzP2p%4ylH~wfExv!+`pk8s}?!j(i+mgrP)GJVGd2L zrifxR3OQ=I9MLCX4Ray;#3LJ#2{HtkfT^vG#S0hk#JUIhwAC2&WDQrS)2nfk*V=iQ!Y2K(sVlqHpM%x^1{ck;y4Kin5jc`W;@8<8AFx4+YWs z?QSo2A}4+Q<6F?#y0F{r Date: Thu, 28 Jan 2010 13:53:28 -0500 Subject: [PATCH 081/170] additional debugging data for Sessions --- classes/Session.php | 43 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/classes/Session.php b/classes/Session.php index 79a69a96ea..2422f8b68e 100644 --- a/classes/Session.php +++ b/classes/Session.php @@ -64,8 +64,12 @@ class Session extends Memcached_DataObject $session = Session::staticGet('id', $id); if (empty($session)) { + self::logdeb("Couldn't find '$id'"); return ''; } else { + self::logdeb("Found '$id', returning " . + strlen($session->session_data) . + " chars of data"); return (string)$session->session_data; } } @@ -77,14 +81,24 @@ class Session extends Memcached_DataObject $session = Session::staticGet('id', $id); if (empty($session)) { + self::logdeb("'$id' doesn't yet exist; inserting."); $session = new Session(); $session->id = $id; $session->session_data = $session_data; $session->created = common_sql_now(); - return $session->insert(); + $result = $session->insert(); + + if (!$result) { + common_log_db_error($session, 'INSERT', __FILE__); + self::logdeb("Failed to insert '$id'."); + } else { + self::logdeb("Successfully inserted '$id' (result = $result)."); + } + return $result; } else { + self::logdeb("'$id' already exists; updating."); if (strcmp($session->session_data, $session_data) == 0) { self::logdeb("Not writing session '$id'; unchanged"); return true; @@ -95,7 +109,16 @@ class Session extends Memcached_DataObject $session->session_data = $session_data; - return $session->update($orig); + $result = $session->update($orig); + + if (!$result) { + common_log_db_error($session, 'UPDATE', __FILE__); + self::logdeb("Failed to update '$id'."); + } else { + self::logdeb("Successfully updated '$id' (result = $result)."); + } + + return $result; } } } @@ -106,8 +129,17 @@ class Session extends Memcached_DataObject $session = Session::staticGet('id', $id); - if (!empty($session)) { - return $session->delete(); + if (empty($session)) { + self::logdeb("Can't find '$id' to delete."); + } else { + $result = $session->delete(); + if (!$result) { + common_log_db_error($session, 'DELETE', __FILE__); + self::logdeb("Failed to delete '$id'."); + } else { + self::logdeb("Successfully deleted '$id' (result = $result)."); + } + return $result; } } @@ -132,7 +164,10 @@ class Session extends Memcached_DataObject $session->free(); + self::logdeb("Found " . count($ids) . " ids to delete."); + foreach ($ids as $id) { + self::logdeb("Destroying session '$id'."); self::destroy($id); } } From 84b5480007d30280cc9c829fe1316db0f853f64c Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 28 Jan 2010 12:57:52 -0500 Subject: [PATCH 082/170] update mysqltimestamps on insert and update --- classes/Memcached_DataObject.php | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/classes/Memcached_DataObject.php b/classes/Memcached_DataObject.php index 2cc6377f83..bc9aaf81fe 100644 --- a/classes/Memcached_DataObject.php +++ b/classes/Memcached_DataObject.php @@ -147,6 +147,7 @@ class Memcached_DataObject extends DB_DataObject { $result = parent::insert(); if ($result) { + $this->fixupTimestamps(); $this->encache(); // in case of cached negative lookups } return $result; @@ -159,6 +160,7 @@ class Memcached_DataObject extends DB_DataObject } $result = parent::update($orig); if ($result) { + $this->fixupTimestamps(); $this->encache(); } return $result; @@ -366,7 +368,7 @@ class Memcached_DataObject extends DB_DataObject } /** - * sends query to database - this is the private one that must work + * sends query to database - this is the private one that must work * - internal functions use this rather than $this->query() * * Overridden to do logging. @@ -529,4 +531,20 @@ class Memcached_DataObject extends DB_DataObject return $c->delete($cacheKey); } + + function fixupTimestamps() + { + // Fake up timestamp columns + $columns = $this->table(); + foreach ($columns as $name => $type) { + if ($type & DB_DATAOBJECT_MYSQLTIMESTAMP) { + $this->$name = common_sql_now(); + } + } + } + + function debugDump() + { + common_debug("debugDump: " . common_log_objstring($this)); + } } From 74a03cbe1fecda9764f826c088331cc4ffbb9433 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 28 Jan 2010 14:27:35 -0500 Subject: [PATCH 083/170] always set up database_rw, regardless, so cached sessions work --- index.php | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/index.php b/index.php index b5edc0f947..5520d690b5 100644 --- a/index.php +++ b/index.php @@ -152,6 +152,16 @@ function checkMirror($action_obj, $args) static $alwaysRW = array('session', 'remember_me'); + // We ensure that these tables always are used + // on the master DB + + $config['db']['database_rw'] = $config['db']['database']; + $config['db']['ini_rw'] = INSTALLDIR.'/classes/statusnet.ini'; + + foreach ($alwaysRW as $table) { + $config['db']['table_'.$table] = 'rw'; + } + if (common_config('db', 'mirror') && $action_obj->isReadOnly($args)) { if (is_array(common_config('db', 'mirror'))) { // "load balancing", ha ha @@ -162,16 +172,6 @@ function checkMirror($action_obj, $args) $mirror = common_config('db', 'mirror'); } - // We ensure that these tables always are used - // on the master DB - - $config['db']['database_rw'] = $config['db']['database']; - $config['db']['ini_rw'] = INSTALLDIR.'/classes/statusnet.ini'; - - foreach ($alwaysRW as $table) { - $config['db']['table_'.$table] = 'rw'; - } - // everyone else uses the mirror $config['db']['database'] = $mirror; From 513f8be07a22d722b86509e570bee46d028066f2 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 28 Jan 2010 16:26:55 -0500 Subject: [PATCH 084/170] hide most DB_DataObject errors --- classes/Memcached_DataObject.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/classes/Memcached_DataObject.php b/classes/Memcached_DataObject.php index e615f23539..f4dfe63141 100644 --- a/classes/Memcached_DataObject.php +++ b/classes/Memcached_DataObject.php @@ -547,4 +547,9 @@ class Memcached_DataObject extends DB_DataObject { common_debug("debugDump: " . common_log_objstring($this)); } + + function raiseError($message, $type = null, $behaviour = null) + { + throw new ServerException("DB_DataObject error [$type]: $message"); + } } From fa7895333724e314e2b32cb89f19a41069c554be Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 28 Jan 2010 16:35:38 -0500 Subject: [PATCH 085/170] move RW setup above user get in index.php so remember_me works --- index.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/index.php b/index.php index 5520d690b5..5aa40440aa 100644 --- a/index.php +++ b/index.php @@ -146,7 +146,7 @@ function formatBacktraceLine($n, $line) return $out; } -function checkMirror($action_obj, $args) +function setupRW() { global $config; @@ -161,7 +161,10 @@ function checkMirror($action_obj, $args) foreach ($alwaysRW as $table) { $config['db']['table_'.$table] = 'rw'; } +} +function checkMirror($action_obj, $args) +{ if (common_config('db', 'mirror') && $action_obj->isReadOnly($args)) { if (is_array(common_config('db', 'mirror'))) { // "load balancing", ha ha @@ -237,9 +240,13 @@ function main() PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, 'handleError'); + // Make sure RW database is setup + + setupRW(); + // XXX: we need a little more structure in this script - // get and cache current user + // get and cache current user (may hit RW!) $user = common_current_user(); From be7bca2303cc9900f2c1a746a10a785d9d95783c Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 28 Jan 2010 16:50:28 -0500 Subject: [PATCH 086/170] Revert "move RW setup above user get in index.php so remember_me works" This reverts commit fa7895333724e314e2b32cb89f19a41069c554be. --- index.php | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/index.php b/index.php index 5aa40440aa..5520d690b5 100644 --- a/index.php +++ b/index.php @@ -146,7 +146,7 @@ function formatBacktraceLine($n, $line) return $out; } -function setupRW() +function checkMirror($action_obj, $args) { global $config; @@ -161,10 +161,7 @@ function setupRW() foreach ($alwaysRW as $table) { $config['db']['table_'.$table] = 'rw'; } -} -function checkMirror($action_obj, $args) -{ if (common_config('db', 'mirror') && $action_obj->isReadOnly($args)) { if (is_array(common_config('db', 'mirror'))) { // "load balancing", ha ha @@ -240,13 +237,9 @@ function main() PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, 'handleError'); - // Make sure RW database is setup - - setupRW(); - // XXX: we need a little more structure in this script - // get and cache current user (may hit RW!) + // get and cache current user $user = common_current_user(); From a33194effb350a03dcdf1c0683fb15d575d245e5 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 28 Jan 2010 16:52:05 -0500 Subject: [PATCH 087/170] Revert "Revert "move RW setup above user get in index.php so remember_me works"" This reverts commit be7bca2303cc9900f2c1a746a10a785d9d95783c. --- index.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/index.php b/index.php index 5520d690b5..5aa40440aa 100644 --- a/index.php +++ b/index.php @@ -146,7 +146,7 @@ function formatBacktraceLine($n, $line) return $out; } -function checkMirror($action_obj, $args) +function setupRW() { global $config; @@ -161,7 +161,10 @@ function checkMirror($action_obj, $args) foreach ($alwaysRW as $table) { $config['db']['table_'.$table] = 'rw'; } +} +function checkMirror($action_obj, $args) +{ if (common_config('db', 'mirror') && $action_obj->isReadOnly($args)) { if (is_array(common_config('db', 'mirror'))) { // "load balancing", ha ha @@ -237,9 +240,13 @@ function main() PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, 'handleError'); + // Make sure RW database is setup + + setupRW(); + // XXX: we need a little more structure in this script - // get and cache current user + // get and cache current user (may hit RW!) $user = common_current_user(); From 63a0e84a8b94d84b106431b648ec76e2537ab9c6 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 28 Jan 2010 16:52:42 -0500 Subject: [PATCH 088/170] lost config in index.php made all traffic go to master --- index.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/index.php b/index.php index 5aa40440aa..605b380bf8 100644 --- a/index.php +++ b/index.php @@ -165,6 +165,8 @@ function setupRW() function checkMirror($action_obj, $args) { + global $config; + if (common_config('db', 'mirror') && $action_obj->isReadOnly($args)) { if (is_array(common_config('db', 'mirror'))) { // "load balancing", ha ha From 155a5d446f96651abf3eb62f9b5748e4bdfa0a76 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Thu, 28 Jan 2010 16:49:32 -0800 Subject: [PATCH 089/170] Manual failover for stomp queues. If an array of multiple servers is put in $config['queue']['stomp_server'], enqueues will pick a random server to send to (failing over automatically if any are down). Queue handling daemons connect all servers so they get events no matter where they were delivered. In case of disconnection, daemons should now handle it gracefully and attempt to reconnect every 60 seconds or so, automatically resubscribing to all queues once it's back up. Can put to 'native' failover for reads as well by disabling $config['stomp']['manual_failover'] = false; but this is untested and may explode in addition to requiring that your ActiveMQ cluster actually be set up to handle its own data distribution. Additionally, can choose which queues to mark as persistent by setting $config['stomp']['persistent'] to an array of queue names. --- lib/default.php | 1 + lib/liberalstomp.php | 24 ++- lib/stompqueuemanager.php | 354 +++++++++++++++++++++++++++++--------- 3 files changed, 299 insertions(+), 80 deletions(-) diff --git a/lib/default.php b/lib/default.php index 8de8b10977..c01508695b 100644 --- a/lib/default.php +++ b/lib/default.php @@ -85,6 +85,7 @@ $default = 'stomp_username' => null, 'stomp_password' => null, 'stomp_persistent' => true, // keep items across queue server restart, if persistence is enabled + 'stomp_manual_failover' => true, // if multiple servers are listed, treat them as separate (enqueue on one randomly, listen on all) 'monitor' => null, // URL to monitor ping endpoint (work in progress) 'softlimit' => '90%', // total size or % of memory_limit at which to restart queue threads gracefully 'debug_memory' => false, // true to spit memory usage to log diff --git a/lib/liberalstomp.php b/lib/liberalstomp.php index c9233843a4..3d38953fd2 100644 --- a/lib/liberalstomp.php +++ b/lib/liberalstomp.php @@ -33,6 +33,22 @@ class LiberalStomp extends Stomp return $this->_socket; } + /** + * Return the host we're currently connected to. + * + * @return string + */ + function getServer() + { + $idx = $this->_currentHost; + if ($idx >= 0) { + $host = $this->_hosts[$idx]; + return "$host[0]:$host[1]"; + } else { + return '[unconnected]'; + } + } + /** * Make socket connection to the server * We also set the stream to non-blocking mode, since we'll be @@ -71,10 +87,12 @@ class LiberalStomp extends Stomp // @fixme this sometimes hangs in blocking mode... // shouldn't we have been idle until we found there's more data? $read = fread($this->_socket, $rb); - if ($read === false) { - $this->_reconnect(); + if ($read === false || ($read === '' && feof($this->_socket))) { + // @fixme possibly attempt an auto reconnect as old code? + throw new StompException("Error reading"); + //$this->_reconnect(); // @fixme this will lose prior items - return $this->readFrames(); + //return $this->readFrames(); } $data .= $read; if (strpos($data, "\x00") !== false) { diff --git a/lib/stompqueuemanager.php b/lib/stompqueuemanager.php index 4e2b586025..ec150bbb6e 100644 --- a/lib/stompqueuemanager.php +++ b/lib/stompqueuemanager.php @@ -29,28 +29,37 @@ */ require_once 'Stomp.php'; +require_once 'Stomp/Exception.php'; class StompQueueManager extends QueueManager { - var $server = null; - var $username = null; - var $password = null; - var $base = null; - var $con = null; + protected $servers; + protected $username; + protected $password; + protected $base; protected $control; + + protected $useTransactions = true; protected $sites = array(); protected $subscriptions = array(); - protected $useTransactions = true; - protected $transaction = null; - protected $transactionCount = 0; + protected $cons = array(); // all open connections + protected $disconnect = array(); + protected $transaction = array(); + protected $transactionCount = array(); + protected $defaultIdx = 0; function __construct() { parent::__construct(); - $this->server = common_config('queue', 'stomp_server'); + $server = common_config('queue', 'stomp_server'); + if (is_array($server)) { + $this->servers = $server; + } else { + $this->servers = array($server); + } $this->username = common_config('queue', 'stomp_username'); $this->password = common_config('queue', 'stomp_password'); $this->base = common_config('queue', 'queue_basename'); @@ -99,9 +108,9 @@ class StompQueueManager extends QueueManager $message .= ':' . $param; } $this->_connect(); - $result = $this->con->send($this->control, - $message, - array ('created' => common_sql_now())); + $result = $this->_send($this->control, + $message, + array ('created' => common_sql_now())); if ($result) { $this->_log(LOG_INFO, "Sent control ping to queue daemons: $message"); return true; @@ -166,29 +175,59 @@ class StompQueueManager extends QueueManager /** * Saves a notice object reference into the queue item table. * @return boolean true on success + * @throws StompException on connection or send error */ public function enqueue($object, $queue) + { + $this->_connect(); + return $this->_doEnqueue($object, $queue, $this->defaultIdx); + } + + /** + * Saves a notice object reference into the queue item table + * on the given connection. + * + * @return boolean true on success + * @throws StompException on connection or send error + */ + protected function _doEnqueue($object, $queue, $idx) { $msg = $this->encode($object); $rep = $this->logrep($object); - $this->_connect(); - $props = array('created' => common_sql_now()); - if (common_config('queue', 'stomp_persistent')) { + if ($this->isPersistent($queue)) { $props['persistent'] = 'true'; } - $result = $this->con->send($this->queueName($queue), - $msg, // BODY of the message - $props); + + $con = $this->cons[$idx]; + $host = $con->getServer(); + $result = $con->send($this->queueName($queue), $msg, $props); if (!$result) { - common_log(LOG_ERR, "Error sending $rep to $queue queue"); + common_log(LOG_ERR, "Error sending $rep to $queue queue on $host"); return false; } - common_log(LOG_DEBUG, "complete remote queueing $rep for $queue"); + common_log(LOG_DEBUG, "complete remote queueing $rep for $queue on $host"); $this->stats('enqueued', $queue); + return true; + } + + /** + * Determine whether messages to this queue should be marked as persistent. + * Actual persistent storage depends on the queue server's configuration. + * @param string $queue + * @return bool + */ + protected function isPersistent($queue) + { + $mode = common_config('queue', 'stomp_persistent'); + if (is_array($mode)) { + return in_array($queue, $mode); + } else { + return (bool)$mode; + } } /** @@ -199,7 +238,29 @@ class StompQueueManager extends QueueManager */ public function getSockets() { - return array($this->con->getSocket()); + $sockets = array(); + foreach ($this->cons as $con) { + if ($con) { + $sockets[] = $con->getSocket(); + } + } + return $sockets; + } + + /** + * Get the Stomp connection object associated with the given socket. + * @param resource $socket + * @return int index into connections list + * @throws Exception + */ + protected function connectionFromSocket($socket) + { + foreach ($this->cons as $i => $con) { + if ($con && $con->getSocket() === $socket) { + return $i; + } + } + throw new Exception(__CLASS__ . " asked to read from unrecognized socket"); } /** @@ -211,27 +272,56 @@ class StompQueueManager extends QueueManager */ public function handleInput($socket) { - assert($socket === $this->con->getSocket()); + $idx = $this->connectionFromSocket($socket); + $con = $this->cons[$idx]; + $host = $con->getServer(); + $ok = true; - $frames = $this->con->readFrames(); + try { + $frames = $con->readFrames(); + } catch (StompException $e) { + common_log(LOG_ERR, "Lost connection to $host: " . $e->getMessage()); + $this->cons[$idx] = null; + $this->transaction[$idx] = null; + $this->disconnect[$idx] = time(); + return false; + } foreach ($frames as $frame) { $dest = $frame->headers['destination']; if ($dest == $this->control) { - if (!$this->handleControlSignal($frame)) { + if (!$this->handleControlSignal($idx, $frame)) { // We got a control event that requests a shutdown; // close out and stop handling anything else! break; } } else { - $ok = $ok && $this->handleItem($frame); + $ok = $ok && $this->handleItem($idx, $frame); } } return $ok; } + /** + * Attempt to reconnect in background if we lost a connection. + */ + function idle() + { + $now = time(); + foreach ($this->cons as $idx => $con) { + if (empty($con)) { + $age = $now - $this->disconnect[$idx]; + if ($age >= 60) { + $this->_reconnect($idx); + } + } + } + return true; + } + /** * Initialize our connection and subscribe to all the queues - * we're going to need to handle... + * we're going to need to handle... If multiple queue servers + * are configured for failover, we'll listen to all of them. * * Side effects: in multi-site mode, may reset site configuration. * @@ -241,10 +331,14 @@ class StompQueueManager extends QueueManager public function start($master) { parent::start($master); - $this->_connect(); + $this->_connectAll(); common_log(LOG_INFO, "Subscribing to $this->control"); - $this->con->subscribe($this->control); + foreach ($this->cons as $con) { + if ($con) { + $con->subscribe($this->control); + } + } if ($this->sites) { foreach ($this->sites as $server) { StatusNet::init($server); @@ -253,7 +347,11 @@ class StompQueueManager extends QueueManager } else { $this->doSubscribe(); } - $this->begin(); + foreach ($this->cons as $i => $con) { + if ($con) { + $this->begin($i); + } + } return true; } @@ -268,8 +366,12 @@ class StompQueueManager extends QueueManager { // If there are any outstanding delivered messages we haven't processed, // free them for another thread to take. - $this->rollback(); - $this->con->unsubscribe($this->control); + foreach ($this->cons as $i => $con) { + if ($con) { + $this->rollback($i); + $con->unsubscribe($this->control); + } + } if ($this->sites) { foreach ($this->sites as $server) { StatusNet::init($server); @@ -291,23 +393,106 @@ class StompQueueManager extends QueueManager } /** - * Lazy open connection to Stomp queue server. + * Lazy open a single connection to Stomp queue server. + * If multiple servers are configured, we let the Stomp client library + * worry about finding a working connection among them. */ protected function _connect() { - if (empty($this->con)) { - $this->_log(LOG_INFO, "Connecting to '$this->server' as '$this->username'..."); - $this->con = new LiberalStomp($this->server); - - if ($this->con->connect($this->username, $this->password)) { - $this->_log(LOG_INFO, "Connected."); + if (empty($this->cons)) { + $list = $this->servers; + if (count($list) > 1) { + shuffle($list); // Randomize to spread load + $url = 'failover://(' . implode(',', $list) . ')'; } else { - $this->_log(LOG_ERR, 'Failed to connect to queue server'); - throw new ServerException('Failed to connect to queue server'); + $url = $list[0]; + } + $con = $this->_doConnect($url); + $this->cons = array($con); + $this->transactionCount = array(0); + $this->transaction = array(null); + $this->disconnect = array(null); + } + } + + /** + * Lazy open connections to all Stomp servers, if in manual failover + * mode. This means the queue servers don't speak to each other, so + * we have to listen to all of them to make sure we get all events. + */ + protected function _connectAll() + { + if (!common_config('queue', 'stomp_manual_failover')) { + return $this->_connect(); + } + if (empty($this->cons)) { + $this->cons = array(); + $this->transactionCount = array(); + $this->transaction = array(); + foreach ($this->servers as $idx => $server) { + try { + $this->cons[] = $this->_doConnect($server); + $this->disconnect[] = null; + } catch (Exception $e) { + // s'okay, we'll live + $this->cons[] = null; + $this->disconnect[] = time(); + } + $this->transactionCount[] = 0; + $this->transaction[] = null; + } + if (empty($this->cons)) { + throw new ServerException("No queue servers reachable..."); + return false; } } } + protected function _reconnect($idx) + { + try { + $con = $this->_doConnect($this->servers[$idx]); + } catch (Exception $e) { + $this->_log(LOG_ERR, $e->getMessage()); + $con = null; + } + if ($con) { + $this->cons[$idx] = $con; + $this->disconnect[$idx] = null; + + // now we have to listen to everything... + // @fixme refactor this nicer. :P + $host = $con->getServer(); + $this->_log(LOG_INFO, "Resubscribing to $this->control on $host"); + $con->subscribe($this->control); + foreach ($this->subscriptions as $site => $queues) { + foreach ($queues as $queue) { + $this->_log(LOG_INFO, "Resubscribing to $queue on $host"); + $con->subscribe($queue); + } + } + $this->begin($idx); + } else { + // Try again later... + $this->disconnect[$idx] = time(); + } + } + + protected function _doConnect($server) + { + $this->_log(LOG_INFO, "Connecting to '$server' as '$this->username'..."); + $con = new LiberalStomp($server); + + if ($con->connect($this->username, $this->password)) { + $this->_log(LOG_INFO, "Connected."); + } else { + $this->_log(LOG_ERR, 'Failed to connect to queue server'); + throw new ServerException('Failed to connect to queue server'); + } + + return $con; + } + /** * Subscribe to all enabled notice queues for the current site. */ @@ -319,7 +504,11 @@ class StompQueueManager extends QueueManager $rawqueue = $this->queueName($queue); $this->subscriptions[$site][$queue] = $rawqueue; $this->_log(LOG_INFO, "Subscribing to $rawqueue"); - $this->con->subscribe($rawqueue); + foreach ($this->cons as $con) { + if ($con) { + $con->subscribe($rawqueue); + } + } } } @@ -333,7 +522,11 @@ class StompQueueManager extends QueueManager if (!empty($this->subscriptions[$site])) { foreach ($this->subscriptions[$site] as $queue => $rawqueue) { $this->_log(LOG_INFO, "Unsubscribing from $rawqueue"); - $this->con->unsubscribe($rawqueue); + foreach ($this->cons as $con) { + if ($con) { + $con->unsubscribe($rawqueue); + } + } unset($this->subscriptions[$site][$queue]); } } @@ -348,27 +541,31 @@ class StompQueueManager extends QueueManager * Side effects: in multi-site mode, may reset site configuration to * match the site that queued the event. * + * @param int $idx connection index * @param StompFrame $frame * @return bool */ - protected function handleItem($frame) + protected function handleItem($idx, $frame) { + $this->defaultIdx = $idx; + list($site, $queue) = $this->parseDestination($frame->headers['destination']); if ($site != $this->currentSite()) { $this->stats('switch'); StatusNet::init($site); } + $host = $this->cons[$idx]->getServer(); if (is_numeric($frame->body)) { $id = intval($frame->body); - $info = "notice $id posted at {$frame->headers['created']} in queue $queue"; + $info = "notice $id posted at {$frame->headers['created']} in queue $queue from $host"; $notice = Notice::staticGet('id', $id); if (empty($notice)) { $this->_log(LOG_WARNING, "Skipping missing $info"); - $this->ack($frame); - $this->commit(); - $this->begin(); + $this->ack($idx, $frame); + $this->commit($idx); + $this->begin($idx); $this->stats('badnotice', $queue); return false; } @@ -376,16 +573,16 @@ class StompQueueManager extends QueueManager $item = $notice; } else { // @fixme should we serialize, or json, or what here? - $info = "string posted at {$frame->headers['created']} in queue $queue"; + $info = "string posted at {$frame->headers['created']} in queue $queue from $host"; $item = $frame->body; } $handler = $this->getHandler($queue); if (!$handler) { $this->_log(LOG_ERR, "Missing handler class; skipping $info"); - $this->ack($frame); - $this->commit(); - $this->begin(); + $this->ack($idx, $frame); + $this->commit($idx); + $this->begin($idx); $this->stats('badhandler', $queue); return false; } @@ -397,18 +594,18 @@ class StompQueueManager extends QueueManager // FIXME we probably shouldn't have to do // this kind of queue management ourselves; // if we don't ack, it should resend... - $this->ack($frame); + $this->ack($idx, $frame); $this->enqueue($item, $queue); - $this->commit(); - $this->begin(); + $this->commit($idx); + $this->begin($idx); $this->stats('requeued', $queue); return false; } $this->_log(LOG_INFO, "Successfully handled $info"); - $this->ack($frame); - $this->commit(); - $this->begin(); + $this->ack($idx, $frame); + $this->commit($idx); + $this->begin($idx); $this->stats('handled', $queue); return true; } @@ -416,10 +613,11 @@ class StompQueueManager extends QueueManager /** * Process a control signal broadcast. * + * @param int $idx connection index * @param array $frame Stomp frame * @return bool true to continue; false to stop further processing. */ - protected function handleControlSignal($frame) + protected function handleControlSignal($idx, $frame) { $message = trim($frame->body); if (strpos($message, ':') !== false) { @@ -443,9 +641,9 @@ class StompQueueManager extends QueueManager $this->_log(LOG_ERR, "Ignoring unrecognized control message: $message"); } - $this->ack($frame); - $this->commit(); - $this->begin(); + $this->ack($idx, $frame); + $this->commit($idx); + $this->begin($idx); return $shutdown; } @@ -522,47 +720,49 @@ class StompQueueManager extends QueueManager common_log($level, 'StompQueueManager: '.$msg); } - protected function begin() + protected function begin($idx) { if ($this->useTransactions) { - if ($this->transaction) { + if (!empty($this->transaction[$idx])) { throw new Exception("Tried to start transaction in the middle of a transaction"); } - $this->transactionCount++; - $this->transaction = $this->master->id . '-' . $this->transactionCount . '-' . time(); - $this->con->begin($this->transaction); + $this->transactionCount[$idx]++; + $this->transaction[$idx] = $this->master->id . '-' . $this->transactionCount[$idx] . '-' . time(); + $this->cons[$idx]->begin($this->transaction[$idx]); } } - protected function ack($frame) + protected function ack($idx, $frame) { if ($this->useTransactions) { - if (!$this->transaction) { + if (empty($this->transaction[$idx])) { throw new Exception("Tried to ack but not in a transaction"); } + $this->cons[$idx]->ack($frame, $this->transaction[$idx]); + } else { + $this->cons[$idx]->ack($frame); } - $this->con->ack($frame, $this->transaction); } - protected function commit() + protected function commit($idx) { if ($this->useTransactions) { - if (!$this->transaction) { + if (empty($this->transaction[$idx])) { throw new Exception("Tried to commit but not in a transaction"); } - $this->con->commit($this->transaction); - $this->transaction = null; + $this->cons[$idx]->commit($this->transaction[$idx]); + $this->transaction[$idx] = null; } } - protected function rollback() + protected function rollback($idx) { if ($this->useTransactions) { - if (!$this->transaction) { + if (empty($this->transaction[$idx])) { throw new Exception("Tried to rollback but not in a transaction"); } - $this->con->commit($this->transaction); - $this->transaction = null; + $this->cons[$idx]->commit($this->transaction[$idx]); + $this->transaction[$idx] = null; } } } From d13d73c5630244963f0c3bd9db68dd6c6451821a Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 28 Jan 2010 18:40:38 -0500 Subject: [PATCH 090/170] Last-chance distribution if enqueueing fails --- classes/Notice.php | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/classes/Notice.php b/classes/Notice.php index 90e3e76ef3..a60dd5bcd7 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -326,13 +326,7 @@ class Notice extends Memcached_DataObject # XXX: someone clever could prepend instead of clearing the cache $notice->blowOnInsert(); - if (common_config('queue', 'inboxes')) { - $qm = QueueManager::get(); - $qm->enqueue($notice, 'distrib'); - } else { - $handler = new DistribQueueHandler(); - $handler->handle($notice); - } + $notice->distribute(); return $notice; } @@ -1447,4 +1441,31 @@ class Notice extends Memcached_DataObject $gi->free(); } + + function distribute() + { + if (common_config('queue', 'inboxes')) { + // If there's a failure, we want to _force_ + // distribution at this point. + try { + $qm = QueueManager::get(); + $qm->enqueue($this, 'distrib'); + } catch (Exception $e) { + // If the exception isn't transient, this + // may throw more exceptions as DQH does + // its own enqueueing. So, we ignore them! + try { + $handler = new DistribQueueHandler(); + $handler->handle($this); + } catch (Exception $e) { + common_log(LOG_ERR, "emergency redistribution resulted in " . $e->getMessage()); + } + // Re-throw so somebody smarter can handle it. + throw $e; + } + } else { + $handler = new DistribQueueHandler(); + $handler->handle($this); + } + } } From 48a1a5a2dcaf026d92caf0656d44f324cd1bbf0c Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Fri, 29 Jan 2010 01:49:38 +0000 Subject: [PATCH 091/170] Adjust API authentication to also check for OAuth protocol params in the HTTP Authorization header, as defined in OAuth HTTP Authorization Scheme. --- lib/apiauth.php | 84 +++++++++++++++++++++++++++++++------------------ 1 file changed, 53 insertions(+), 31 deletions(-) diff --git a/lib/apiauth.php b/lib/apiauth.php index c684a6caee..99500404f9 100644 --- a/lib/apiauth.php +++ b/lib/apiauth.php @@ -55,11 +55,10 @@ class ApiAuthAction extends ApiAction { var $auth_user_nickname = null; var $auth_user_password = null; - var $access_token = null; - var $oauth_source = null; /** - * Take arguments for running, and output basic auth header if needed + * Take arguments for running, looks for an OAuth request, + * and outputs basic auth header if needed * * @param array $args $_REQUEST args * @@ -71,26 +70,23 @@ class ApiAuthAction extends ApiAction { parent::prepare($args); - $this->consumer_key = $this->arg('oauth_consumer_key'); - $this->access_token = $this->arg('oauth_token'); - // NOTE: $this->auth_user has to get set in prepare(), not handle(), // because subclasses do stuff with it in their prepares. if ($this->requiresAuth()) { - if (!empty($this->access_token)) { - $this->checkOAuthRequest(); - } else { + + $oauthReq = $this->getOAuthRequest(); + + if (!$oauthReq) { $this->checkBasicAuthUser(true); + } else { + $this->checkOAuthRequest($oauthReq); } } else { // Check to see if a basic auth user is there even // if one's not required - - if (empty($this->access_token)) { - $this->checkBasicAuthUser(false); - } + $this->checkBasicAuthUser(false); } // Reject API calls with the wrong access level @@ -107,12 +103,44 @@ class ApiAuthAction extends ApiAction return true; } - function handle($args) + /** + * Determine whether the request is an OAuth request. + * This is to avoid doign any unnecessary DB lookups. + * + * @return mixed the OAuthRequest or false + * + */ + + function getOAuthRequest() { - parent::handle($args); + ApiOauthAction::cleanRequest(); + + $req = OAuthRequest::from_request(); + + $consumer = $req->get_parameter('oauth_consumer_key'); + $accessToken = $req->get_parameter('oauth_token'); + + // XXX: Is it good enough to assume it's not meant to be an + // OAuth request if there is no consumer or token? --Z + + if (empty($consumer) || empty($accessToken)) { + return false; + } + + return $req; } - function checkOAuthRequest() + /** + * Verifies the OAuth request signature, sets the auth user + * and access type (read-only or read-write) + * + * @param OAuthRequest $request the OAuth Request + * + * @return nothing + * + */ + + function checkOAuthRequest($request) { $datastore = new ApiStatusNetOAuthDataStore(); $server = new OAuthServer($datastore); @@ -120,22 +148,19 @@ class ApiAuthAction extends ApiAction $server->add_signature_method($hmac_method); - ApiOauthAction::cleanRequest(); - try { - $req = OAuthRequest::from_request(); - $server->verify_request($req); + $server->verify_request($request); - $app = Oauth_application::getByConsumerKey($this->consumer_key); + $consumer = $request->get_parameter('oauth_consumer_key'); + $access_token = $request->get_parameter('oauth_token'); + + $app = Oauth_application::getByConsumerKey($consumer); if (empty($app)) { - - // this should probably not happen common_log(LOG_WARNING, 'Couldn\'t find the OAuth app for consumer key: ' . - $this->consumer_key); - + $consumer); throw new OAuthException('No application for that consumer key.'); } @@ -143,11 +168,7 @@ class ApiAuthAction extends ApiAction $this->oauth_source = $app->name; - $appUser = Oauth_application_user::staticGet('token', - $this->access_token); - - // XXX: Check that app->id and appUser->application_id and consumer all - // match? + $appUser = Oauth_application_user::staticGet('token', $access_token); if (!empty($appUser)) { @@ -161,6 +182,8 @@ class ApiAuthAction extends ApiAction $this->access = ($appUser->access_type & Oauth_application::$writeAccess) ? self::READ_WRITE : self::READ_ONLY; + // Set the auth user + if (Event::handle('StartSetApiUser', array(&$user))) { $this->auth_user = User::staticGet('id', $appUser->profile_id); Event::handle('EndSetApiUser', array($user)); @@ -177,7 +200,6 @@ class ApiAuthAction extends ApiAction ($this->access = self::READ_WRITE) ? 'read-write' : 'read-only' )); - return; } else { throw new OAuthException('Bad access token.'); } From 4a0413c0270bcac456c20972342c2c29182bec4e Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 28 Jan 2010 01:42:52 -0500 Subject: [PATCH 092/170] Add a script to set tags for sites --- scripts/settag.php | 84 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 scripts/settag.php diff --git a/scripts/settag.php b/scripts/settag.php new file mode 100644 index 0000000000..1d7b60b902 --- /dev/null +++ b/scripts/settag.php @@ -0,0 +1,84 @@ +#!/usr/bin/env php +. + */ + +define('INSTALLDIR', realpath(dirname(__FILE__) . '/..')); + +$shortoptions = 'd'; +$longoptions = array('delete'); + +$helptext = << +Set the tag for site . + +With -d, delete the tag. + +END_OF_SETTAG_HELP; + +require_once INSTALLDIR.'/scripts/commandline.inc'; + +if (count($args) != 2) { + show_help(); + exit(1); +} + +$nickname = $args[0]; +$tag = strtolower($args[1]); + +$sn = Status_network::memGet('nickname', $nickname); + +if (empty($sn)) { + print "No such site.\n"; + exit(-1); +} + +$tags = $sn->getTags(); + +$i = array_search($tags, $tag); + +if ($i !== false) { + if (have_option('d', 'delete')) { // Delete + unset($tags[$i]); + + $orig = clone($sn); + $sn->tags = implode('|', $tags); + $result = $sn->update($orig); + if (!$result) { + print "Couldn't update.\n"; + exit(-1); + } + } else { + print "Already set.\n"; + exit(-1); + } +} else { + if (have_option('d', 'delete')) { // Delete + print "No such tag.\n"; + exit(-1); + } else { + $tags[] = $tag; + $orig = clone($sn); + $sn->tags = implode('|', $tags); + $result = $sn->update($orig); + if (!$result) { + print "Couldn't update.\n"; + exit(-1); + } + } +} From 864ce8e276220262ef8a26a9138c929145ccf57e Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Thu, 28 Jan 2010 20:09:17 -0800 Subject: [PATCH 093/170] Fixes for status_network db object .ini and tag setter script --- classes/status_network.ini | 1 + scripts/settag.php | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/classes/status_network.ini b/classes/status_network.ini index 8123265e46..adb71cba77 100644 --- a/classes/status_network.ini +++ b/classes/status_network.ini @@ -11,6 +11,7 @@ theme = 2 logo = 2 created = 142 modified = 384 +tags = 34 [status_network__keys] nickname = K diff --git a/scripts/settag.php b/scripts/settag.php index 1d7b60b902..e91d5eb505 100644 --- a/scripts/settag.php +++ b/scripts/settag.php @@ -50,7 +50,7 @@ if (empty($sn)) { $tags = $sn->getTags(); -$i = array_search($tags, $tag); +$i = array_search($tag, $tags); if ($i !== false) { if (have_option('d', 'delete')) { // Delete From 440ee00b1ed9c1b0b552c14b72c962a79e1a402e Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Thu, 28 Jan 2010 22:04:14 -0800 Subject: [PATCH 094/170] Move sessions settings to its own panel --- actions/sessionsadminpanel.php | 201 +++++++++++++++++++++++++++++++++ actions/useradminpanel.php | 20 ---- lib/adminpanelaction.php | 11 +- lib/default.php | 2 +- lib/router.php | 3 +- 5 files changed, 212 insertions(+), 25 deletions(-) create mode 100644 actions/sessionsadminpanel.php diff --git a/actions/sessionsadminpanel.php b/actions/sessionsadminpanel.php new file mode 100644 index 0000000000..4386ef844b --- /dev/null +++ b/actions/sessionsadminpanel.php @@ -0,0 +1,201 @@ +. + * + * @category Settings + * @package StatusNet + * @author Zach Copley + * @copyright 2010 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); +} + +/** + * Admin site sessions + * + * @category Admin + * @package StatusNet + * @author Zach Copley + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +class SessionsadminpanelAction extends AdminPanelAction +{ + /** + * Returns the page title + * + * @return string page title + */ + + function title() + { + return _('Sessions'); + } + + /** + * Instructions for using this form. + * + * @return string instructions + */ + + function getInstructions() + { + return _('Session settings for this StatusNet site.'); + } + + /** + * Show the site admin panel form + * + * @return void + */ + + function showForm() + { + $form = new SessionsAdminPanelForm($this); + $form->show(); + return; + } + + /** + * Save settings from the form + * + * @return void + */ + + function saveSettings() + { + static $booleans = array('sessions' => array('handle', 'debug')); + + $values = array(); + + foreach ($booleans as $section => $parts) { + foreach ($parts as $setting) { + $values[$section][$setting] = ($this->boolean($setting)) ? 1 : 0; + } + } + + // This throws an exception on validation errors + + $this->validate($values); + + // assert(all values are valid); + + $config = new Config(); + + $config->query('BEGIN'); + + foreach ($booleans as $section => $parts) { + foreach ($parts as $setting) { + Config::save($section, $setting, $values[$section][$setting]); + } + } + + $config->query('COMMIT'); + + return; + } + + function validate(&$values) + { + // stub + } +} + +class SessionsAdminPanelForm extends AdminForm +{ + /** + * ID of the form + * + * @return int ID of the form + */ + + function id() + { + return 'sessionsadminpanel'; + } + + /** + * class of the form + * + * @return string class of the form + */ + + function formClass() + { + return 'form_settings'; + } + + /** + * Action of the form + * + * @return string URL of the action + */ + + function action() + { + return common_local_url('sessionsadminpanel'); + } + + /** + * Data elements of the form + * + * @return void + */ + + function formData() + { + $this->out->elementStart('fieldset', array('id' => 'settings_user_sessions')); + $this->out->element('legend', null, _('Sessions')); + + $this->out->elementStart('ul', 'form_data'); + + $this->li(); + $this->out->checkbox('handle', _('Handle sessions'), + (bool) $this->value('handle', 'sessions'), + _('Whether to handle sessions ourselves.')); + $this->unli(); + + $this->li(); + $this->out->checkbox('debug', _('Session debugging'), + (bool) $this->value('debug', 'sessions'), + _('Turn on debugging output for sessions.')); + $this->unli(); + + $this->out->elementEnd('ul'); + + $this->out->elementEnd('fieldset'); + } + + /** + * Action elements + * + * @return void + */ + + function formActions() + { + $this->out->submit('submit', _('Save'), 'submit', null, _('Save site settings')); + } +} diff --git a/actions/useradminpanel.php b/actions/useradminpanel.php index 5de2db5fff..6813222f5f 100644 --- a/actions/useradminpanel.php +++ b/actions/useradminpanel.php @@ -96,7 +96,6 @@ class UseradminpanelAction extends AdminPanelAction ); static $booleans = array( - 'sessions' => array('handle', 'debug'), 'invite' => array('enabled') ); @@ -261,26 +260,7 @@ class UserAdminPanelForm extends AdminForm $this->out->elementEnd('ul'); $this->out->elementEnd('fieldset'); - $this->out->elementStart('fieldset', array('id' => 'settings_user_sessions')); - $this->out->element('legend', null, _('Sessions')); - $this->out->elementStart('ul', 'form_data'); - - $this->li(); - $this->out->checkbox('sessions-handle', _('Handle sessions'), - (bool) $this->value('handle', 'sessions'), - _('Whether to handle sessions ourselves.')); - $this->unli(); - - $this->li(); - $this->out->checkbox('sessions-debug', _('Session debugging'), - (bool) $this->value('debug', 'sessions'), - _('Turn on debugging output for sessions.')); - $this->unli(); - - $this->out->elementEnd('ul'); - - $this->out->elementEnd('fieldset'); } diff --git a/lib/adminpanelaction.php b/lib/adminpanelaction.php index f62bfa458a..f05627b317 100644 --- a/lib/adminpanelaction.php +++ b/lib/adminpanelaction.php @@ -327,9 +327,14 @@ class AdminPanelNav extends Widget _('Access configuration'), $action_name == 'accessadminpanel', 'nav_design_admin_panel'); } - if ($this->canAdmin('paths')) { - $this->out->menuItem(common_local_url('pathsadminpanel'), _('Paths'), - _('Paths configuration'), $action_name == 'pathsadminpanel', 'nav_design_admin_panel'); + if ($this->canAdmin('paths')) { + $this->out->menuItem(common_local_url('pathsadminpanel'), _('Paths'), + _('Paths configuration'), $action_name == 'pathsadminpanel', 'nav_design_admin_panel'); + } + + if ($this->canAdmin('sessions')) { + $this->out->menuItem(common_local_url('sessionsadminpanel'), _('Sessions'), + _('Sessions configuration'), $action_name == 'sessionsadminpanel', 'nav_design_admin_panel'); } Event::handle('EndAdminPanelNav', array($this)); diff --git a/lib/default.php b/lib/default.php index c01508695b..1337a96336 100644 --- a/lib/default.php +++ b/lib/default.php @@ -266,7 +266,7 @@ $default = 'OpenID' => null), ), 'admin' => - array('panels' => array('design', 'site', 'user', 'paths', 'access')), + array('panels' => array('design', 'site', 'user', 'paths', 'access', 'sessions')), 'singleuser' => array('enabled' => false, 'nickname' => null), diff --git a/lib/router.php b/lib/router.php index 03765b39dd..be9cfac0c2 100644 --- a/lib/router.php +++ b/lib/router.php @@ -637,8 +637,9 @@ class Router $m->connect('admin/site', array('action' => 'siteadminpanel')); $m->connect('admin/design', array('action' => 'designadminpanel')); $m->connect('admin/user', array('action' => 'useradminpanel')); - $m->connect('admin/access', array('action' => 'accessadminpanel')); + $m->connect('admin/access', array('action' => 'accessadminpanel')); $m->connect('admin/paths', array('action' => 'pathsadminpanel')); + $m->connect('admin/sessions', array('action' => 'sessionsadminpanel')); $m->connect('getfile/:filename', array('action' => 'getfile'), From 288dc3452f3c274d2165d8e7d502631a6eacc97c Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Thu, 28 Jan 2010 22:05:14 -0800 Subject: [PATCH 095/170] Log exceptions from queuedaemon.php if they're not already caught --- scripts/queuedaemon.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/scripts/queuedaemon.php b/scripts/queuedaemon.php index c2e2351c39..30a8a96026 100755 --- a/scripts/queuedaemon.php +++ b/scripts/queuedaemon.php @@ -109,7 +109,13 @@ class QueueDaemon extends SpawningDaemon $master = new QueueMaster($this->get_id()); $master->init($this->all); - $master->service(); + try { + $master->service(); + } catch (Exception $e) { + common_log(LOG_ERR, "Unhandled exception: " . $e->getMessage() . ' ' . + str_replace("\n", " ", $e->getTraceAsString())); + return self::EXIT_ERR; + } $this->log(LOG_INFO, 'finished servicing the queue'); From ccb678ad15ee57302c751ea995264415c64ad298 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Thu, 28 Jan 2010 22:26:58 -0800 Subject: [PATCH 096/170] Wrap each bit of distrib queue handler's saving operation in a try/catch; log exceptions but let everything else continue. --- lib/distribqueuehandler.php | 55 +++++++++++++++++++++++++++++++------ 1 file changed, 46 insertions(+), 9 deletions(-) diff --git a/lib/distribqueuehandler.php b/lib/distribqueuehandler.php index f458d238da..4477468d0a 100644 --- a/lib/distribqueuehandler.php +++ b/lib/distribqueuehandler.php @@ -62,23 +62,60 @@ class DistribQueueHandler { // XXX: do we need to change this for remote users? - $notice->saveTags(); + try { + $notice->saveTags(); + } catch (Exception $e) { + $this->logit($notice, $e); + } - $groups = $notice->saveGroups(); + try { + $groups = $notice->saveGroups(); + } catch (Exception $e) { + $this->logit($notice, $e); + } - $recipients = $notice->saveReplies(); + try { + $recipients = $notice->saveReplies(); + } catch (Exception $e) { + $this->logit($notice, $e); + } - $notice->addToInboxes($groups, $recipients); + try { + $notice->addToInboxes($groups, $recipients); + } catch (Exception $e) { + $this->logit($notice, $e); + } - $notice->saveUrls(); + try { + $notice->saveUrls(); + } catch (Exception $e) { + $this->logit($notice, $e); + } - Event::handle('EndNoticeSave', array($notice)); + try { + Event::handle('EndNoticeSave', array($notice)); + // Enqueue for other handlers + } catch (Exception $e) { + $this->logit($notice, $e); + } - // Enqueue for other handlers - - common_enqueue_notice($notice); + try { + common_enqueue_notice($notice); + } catch (Exception $e) { + $this->logit($notice, $e); + } return true; } + + protected function logit($notice, $e) + { + common_log(LOG_ERR, "Distrib queue exception saving notice $notice->id: " . + $e->getMessage() . ' ' . + str_replace("\n", " ", $e->getTraceAsString())); + + // We'll still return true so we don't get stuck in a loop + // trying to run a bad insert over and over... + } } From e5eca9bd2ce11633e14a840cb93adc6fd3ec8fc0 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Thu, 28 Jan 2010 22:51:07 -0800 Subject: [PATCH 097/170] Don't attempt to resend XMPP messages that can't be broadcast due to the profile being deleted. --- lib/jabber.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/jabber.php b/lib/jabber.php index b6b23521bd..e1bf06ba66 100644 --- a/lib/jabber.php +++ b/lib/jabber.php @@ -358,7 +358,7 @@ function jabber_broadcast_notice($notice) common_log(LOG_WARNING, 'Refusing to broadcast notice with ' . 'unknown profile ' . common_log_objstring($notice), __FILE__); - return false; + return true; // not recoverable; discard. } $msg = jabber_format_notice($profile, $notice); @@ -437,7 +437,7 @@ function jabber_public_notice($notice) common_log(LOG_WARNING, 'Refusing to broadcast notice with ' . 'unknown profile ' . common_log_objstring($notice), __FILE__); - return false; + return true; // not recoverable; discard. } $msg = jabber_format_notice($profile, $notice); From 4d3808a815dd8a020cf17151e4c04a821790169d Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Thu, 28 Jan 2010 23:08:36 -0800 Subject: [PATCH 098/170] Fix more fatal errors in queue edge cases --- lib/api.php | 2 +- lib/jabberqueuehandler.php | 2 +- lib/ombqueuehandler.php | 2 +- lib/publicqueuehandler.php | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/api.php b/lib/api.php index 794b140507..10a2fae28c 100644 --- a/lib/api.php +++ b/lib/api.php @@ -298,7 +298,7 @@ class ApiAction extends Action } } - if ($include_user) { + if ($include_user && $profile) { # Don't get notice (recursive!) $twitter_user = $this->twitterUserArray($profile, false); $twitter_status['user'] = $twitter_user; diff --git a/lib/jabberqueuehandler.php b/lib/jabberqueuehandler.php index 83471f2df7..d6b4b7416a 100644 --- a/lib/jabberqueuehandler.php +++ b/lib/jabberqueuehandler.php @@ -40,7 +40,7 @@ class JabberQueueHandler extends QueueHandler try { return jabber_broadcast_notice($notice); } catch (XMPPHP_Exception $e) { - $this->log(LOG_ERR, "Got an XMPPHP_Exception: " . $e->getMessage()); + common_log(LOG_ERR, "Got an XMPPHP_Exception: " . $e->getMessage()); return false; } } diff --git a/lib/ombqueuehandler.php b/lib/ombqueuehandler.php index 24896c784c..1921c2bacd 100644 --- a/lib/ombqueuehandler.php +++ b/lib/ombqueuehandler.php @@ -39,7 +39,7 @@ class OmbQueueHandler extends QueueHandler function handle($notice) { if ($this->is_remote($notice)) { - $this->log(LOG_DEBUG, 'Ignoring remote notice ' . $notice->id); + common_log(LOG_DEBUG, 'Ignoring remote notice ' . $notice->id); return true; } else { require_once(INSTALLDIR.'/lib/omb.php'); diff --git a/lib/publicqueuehandler.php b/lib/publicqueuehandler.php index c9edb8d5d7..a497d13850 100644 --- a/lib/publicqueuehandler.php +++ b/lib/publicqueuehandler.php @@ -38,7 +38,7 @@ class PublicQueueHandler extends QueueHandler try { return jabber_public_notice($notice); } catch (XMPPHP_Exception $e) { - $this->log(LOG_ERR, "Got an XMPPHP_Exception: " . $e->getMessage()); + common_log(LOG_ERR, "Got an XMPPHP_Exception: " . $e->getMessage()); return false; } } From efb39347b28e2c16fd297c61fae0606a28398dd7 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Fri, 29 Jan 2010 15:20:14 +0000 Subject: [PATCH 099/170] Hides .author from XHR response in showstream --- theme/base/css/display.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/theme/base/css/display.css b/theme/base/css/display.css index 3c51deb312..0d6395d057 100644 --- a/theme/base/css/display.css +++ b/theme/base/css/display.css @@ -995,6 +995,9 @@ padding-left:28px; .notice .author { margin-right:11px; } +#showstream #content .notice .author { +display:none; +} .fn { overflow:hidden; From f6c8b8a8680d99afe37accf8eda29e6660bf69ff Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Fri, 29 Jan 2010 15:20:14 +0000 Subject: [PATCH 100/170] Hides .author from XHR response in showstream --- theme/base/css/display.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/theme/base/css/display.css b/theme/base/css/display.css index 3c51deb312..0d6395d057 100644 --- a/theme/base/css/display.css +++ b/theme/base/css/display.css @@ -995,6 +995,9 @@ padding-left:28px; .notice .author { margin-right:11px; } +#showstream #content .notice .author { +display:none; +} .fn { overflow:hidden; From b68a21d4f77f7d05f12bf85068c2d0d1a0ba3a36 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Fri, 29 Jan 2010 15:43:37 +0000 Subject: [PATCH 101/170] Adds notice author's name to @title in Realtime response --- plugins/Realtime/realtimeupdate.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/Realtime/realtimeupdate.js b/plugins/Realtime/realtimeupdate.js index 52151f9de8..fb9dcdbfb7 100644 --- a/plugins/Realtime/realtimeupdate.js +++ b/plugins/Realtime/realtimeupdate.js @@ -132,11 +132,11 @@ RealtimeUpdate = { user = data['user']; html = data['html'].replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"').replace(/&/g,'&'); source = data['source'].replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"').replace(/&/g,'&'); - +console.log(data); ni = "
  • "+ "
    "+ ""+ - ""+ + ""+ "\""+user['screen_name']+"\"/"+ ""+user['screen_name']+""+ ""+ From f6eecf02fc9eca0d3947a8cacf374909003dc8d4 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Fri, 29 Jan 2010 15:01:21 -0500 Subject: [PATCH 102/170] add simple cache getter/setter static functions to Memcached_DataObject --- classes/Memcached_DataObject.php | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/classes/Memcached_DataObject.php b/classes/Memcached_DataObject.php index f4dfe63141..ab65c30ce2 100644 --- a/classes/Memcached_DataObject.php +++ b/classes/Memcached_DataObject.php @@ -552,4 +552,30 @@ class Memcached_DataObject extends DB_DataObject { throw new ServerException("DB_DataObject error [$type]: $message"); } + + static function cacheGet($keyPart) + { + $c = self::memcache(); + + if (empty($c)) { + return false; + } + + $cacheKey = common_cache_key($keyPart); + + return $c->get($cacheKey); + } + + static function cacheSet($keyPart, $value) + { + $c = self::memcache(); + + if (empty($c)) { + return false; + } + + $cacheKey = common_cache_key($keyPart); + + return $c->set($cacheKey, $value); + } } From d437b76ed1a7ba3c39f0d3cb8bef15e19c1c184f Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Fri, 29 Jan 2010 15:15:04 -0500 Subject: [PATCH 103/170] define a constant for the 'owner' role of a site --- classes/Profile_role.php | 1 + 1 file changed, 1 insertion(+) diff --git a/classes/Profile_role.php b/classes/Profile_role.php index 74aca37305..bf2c453ed0 100644 --- a/classes/Profile_role.php +++ b/classes/Profile_role.php @@ -48,6 +48,7 @@ class Profile_role extends Memcached_DataObject return Memcached_DataObject::pkeyGet('Profile_role', $kv); } + const OWNER = 'owner'; const MODERATOR = 'moderator'; const ADMINISTRATOR = 'administrator'; const SANDBOXED = 'sandboxed'; From 70a4f8c0e26bfdb76f595ce501c6e84a8011fea8 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Fri, 29 Jan 2010 15:15:23 -0500 Subject: [PATCH 104/170] method to get the site owner --- classes/User.php | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/classes/User.php b/classes/User.php index 6ea975202d..b700496178 100644 --- a/classes/User.php +++ b/classes/User.php @@ -925,4 +925,30 @@ class User extends Memcached_DataObject return $share; } } + + static function siteOwner() + { + $owner = self::cacheGet('user:site_owner'); + + if ($owner === false) { // cache miss + + $pr = new Profile_role(); + + $pr->role = Profile_role::OWNER; + + $pr->orderBy('created'); + + $pr->limit(0, 1); + + if ($pr->fetch($true)) { + $owner = User::staticGet('id', $pr->profile_id); + } else { + $owner = null; + } + + self::cacheSet('user:site_owner', $owner); + } + + return $owner; + } } From a7b2a08c42347d7beac43980a673b434a9c0331a Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Fri, 29 Jan 2010 15:15:52 -0500 Subject: [PATCH 105/170] for single-user mode, retrieve either site owner or defined nickname --- lib/router.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/router.php b/lib/router.php index be9cfac0c2..ca9f328126 100644 --- a/lib/router.php +++ b/lib/router.php @@ -649,7 +649,16 @@ class Router if (common_config('singleuser', 'enabled')) { - $nickname = common_config('singleuser', 'nickname'); + $user = User::siteOwner(); + + if (!empty($user)) { + $nickname = $user->nickname; + } else { + $nickname = common_config('singleuser', 'nickname'); + if (empty($nickname)) { + throw new ServerException(_("No single user defined for single-user mode.")); + } + } foreach (array('subscriptions', 'subscribers', 'all', 'foaf', 'xrds', From 2a054a50fb404b01512a00872ab0f68481b0f470 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Fri, 29 Jan 2010 15:33:35 -0500 Subject: [PATCH 106/170] live fast, die young in bash scripts --- scripts/delete_status_network.sh | 4 ++++ scripts/setup_status_network.sh | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/scripts/delete_status_network.sh b/scripts/delete_status_network.sh index f55f1486bb..3a8ebdcfd4 100755 --- a/scripts/delete_status_network.sh +++ b/scripts/delete_status_network.sh @@ -1,5 +1,9 @@ #!/bin/bash +# live fast! die young! + +set -e + source /etc/statusnet/setup.cfg export nickname=$1 diff --git a/scripts/setup_status_network.sh b/scripts/setup_status_network.sh index f502a169a2..4ad808011c 100755 --- a/scripts/setup_status_network.sh +++ b/scripts/setup_status_network.sh @@ -1,5 +1,9 @@ #!/bin/bash +# live fast! die young! + +set -e + source /etc/statusnet/setup.cfg # setup_status_net.sh mysite 'My Site' '1user' 'owner@example.com' 'Firsty McLastname' From 8cb8b357a4383f7afb1e09fcd264aa41ac1502a6 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Fri, 29 Jan 2010 17:54:54 -0500 Subject: [PATCH 107/170] add hooks for user registration --- EVENTS.txt | 9 +++ classes/User.php | 199 ++++++++++++++++++++++++----------------------- 2 files changed, 112 insertions(+), 96 deletions(-) diff --git a/EVENTS.txt b/EVENTS.txt index 1ed670697b..3317c80dec 100644 --- a/EVENTS.txt +++ b/EVENTS.txt @@ -699,3 +699,12 @@ StartShowContentLicense: Showing the default license for content EndShowContentLicense: Showing the default license for content - $action: the current action + +StartUserRegister: When a new user is being registered +- &$profile: new profile data (no ID) +- &$user: new user account (no ID or URI) + +EndUserRegister: When a new user has been registered +- &$profile: new profile data +- &$user: new user account + diff --git a/classes/User.php b/classes/User.php index b700496178..022044aac1 100644 --- a/classes/User.php +++ b/classes/User.php @@ -209,8 +209,6 @@ class User extends Memcached_DataObject $profile = new Profile(); - $profile->query('BEGIN'); - if(!empty($email)) { $email = common_canonical_email($email); @@ -220,7 +218,7 @@ class User extends Memcached_DataObject $profile->nickname = $nickname; if(! User::allowed_nickname($nickname)){ common_log(LOG_WARNING, sprintf("Attempted to register a nickname that is not allowed: %s", $profile->nickname), - __FILE__); + __FILE__); } $profile->profileurl = common_profile_url($nickname); @@ -248,16 +246,8 @@ class User extends Memcached_DataObject $profile->created = common_sql_now(); - $id = $profile->insert(); - - if (empty($id)) { - common_log_db_error($profile, 'INSERT', __FILE__); - return false; - } - $user = new User(); - $user->id = $id; $user->nickname = $nickname; if (!empty($password)) { // may not have a password for OpenID users @@ -282,109 +272,126 @@ class User extends Memcached_DataObject $user->inboxed = 1; $user->created = common_sql_now(); - $user->uri = common_user_uri($user); - $result = $user->insert(); + if (Event::handle('StartUserRegister', array(&$user, &$profile))) { - if (!$result) { - common_log_db_error($user, 'INSERT', __FILE__); - return false; - } + $profile->query('BEGIN'); - // Everyone gets an inbox + $id = $profile->insert(); - $inbox = new Inbox(); - - $inbox->user_id = $user->id; - $inbox->notice_ids = ''; - - $result = $inbox->insert(); - - if (!$result) { - common_log_db_error($inbox, 'INSERT', __FILE__); - return false; - } - - // Everyone is subscribed to themself - - $subscription = new Subscription(); - $subscription->subscriber = $user->id; - $subscription->subscribed = $user->id; - $subscription->created = $user->created; - - $result = $subscription->insert(); - - if (!$result) { - common_log_db_error($subscription, 'INSERT', __FILE__); - return false; - } - - if (!empty($email) && !$user->email) { - - $confirm = new Confirm_address(); - $confirm->code = common_confirmation_code(128); - $confirm->user_id = $user->id; - $confirm->address = $email; - $confirm->address_type = 'email'; - - $result = $confirm->insert(); - if (!$result) { - common_log_db_error($confirm, 'INSERT', __FILE__); + if (empty($id)) { + common_log_db_error($profile, 'INSERT', __FILE__); return false; } - } - if (!empty($code) && $user->email) { - $user->emailChanged(); - } + $user->id = $id; + $user->uri = common_user_uri($user); - // Default system subscription + $result = $user->insert(); - $defnick = common_config('newuser', 'default'); + if (!$result) { + common_log_db_error($user, 'INSERT', __FILE__); + return false; + } - if (!empty($defnick)) { - $defuser = User::staticGet('nickname', $defnick); - if (empty($defuser)) { - common_log(LOG_WARNING, sprintf("Default user %s does not exist.", $defnick), - __FILE__); - } else { - $defsub = new Subscription(); - $defsub->subscriber = $user->id; - $defsub->subscribed = $defuser->id; - $defsub->created = $user->created; + // Everyone gets an inbox - $result = $defsub->insert(); + $inbox = new Inbox(); + + $inbox->user_id = $user->id; + $inbox->notice_ids = ''; + + $result = $inbox->insert(); + + if (!$result) { + common_log_db_error($inbox, 'INSERT', __FILE__); + return false; + } + + // Everyone is subscribed to themself + + $subscription = new Subscription(); + $subscription->subscriber = $user->id; + $subscription->subscribed = $user->id; + $subscription->created = $user->created; + + $result = $subscription->insert(); + + if (!$result) { + common_log_db_error($subscription, 'INSERT', __FILE__); + return false; + } + + if (!empty($email) && !$user->email) { + + $confirm = new Confirm_address(); + $confirm->code = common_confirmation_code(128); + $confirm->user_id = $user->id; + $confirm->address = $email; + $confirm->address_type = 'email'; + + $result = $confirm->insert(); if (!$result) { - common_log_db_error($defsub, 'INSERT', __FILE__); + common_log_db_error($confirm, 'INSERT', __FILE__); return false; } } - } - - $profile->query('COMMIT'); - - if (!empty($email) && !$user->email) { - mail_confirm_address($user, $confirm->code, $profile->nickname, $email); - } - - // Welcome message - - $welcome = common_config('newuser', 'welcome'); - - if (!empty($welcome)) { - $welcomeuser = User::staticGet('nickname', $welcome); - if (empty($welcomeuser)) { - common_log(LOG_WARNING, sprintf("Welcome user %s does not exist.", $defnick), - __FILE__); - } else { - $notice = Notice::saveNew($welcomeuser->id, - sprintf(_('Welcome to %1$s, @%2$s!'), - common_config('site', 'name'), - $user->nickname), - 'system'); + if (!empty($code) && $user->email) { + $user->emailChanged(); } + + // Default system subscription + + $defnick = common_config('newuser', 'default'); + + if (!empty($defnick)) { + $defuser = User::staticGet('nickname', $defnick); + if (empty($defuser)) { + common_log(LOG_WARNING, sprintf("Default user %s does not exist.", $defnick), + __FILE__); + } else { + $defsub = new Subscription(); + $defsub->subscriber = $user->id; + $defsub->subscribed = $defuser->id; + $defsub->created = $user->created; + + $result = $defsub->insert(); + + if (!$result) { + common_log_db_error($defsub, 'INSERT', __FILE__); + return false; + } + } + } + + $profile->query('COMMIT'); + + if (!empty($email) && !$user->email) { + mail_confirm_address($user, $confirm->code, $profile->nickname, $email); + } + + // Welcome message + + $welcome = common_config('newuser', 'welcome'); + + if (!empty($welcome)) { + $welcomeuser = User::staticGet('nickname', $welcome); + if (empty($welcomeuser)) { + common_log(LOG_WARNING, sprintf("Welcome user %s does not exist.", $defnick), + __FILE__); + } else { + $notice = Notice::saveNew($welcomeuser->id, + sprintf(_('Welcome to %1$s, @%2$s!'), + common_config('site', 'name'), + $user->nickname), + 'system'); + + } + } + + Event::handle('EndUserRegister', array(&$profile, &$user)); } return $user; From 8318f195a2997b4e3a4831d65685dca24a2b66aa Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Fri, 29 Jan 2010 18:29:51 -0500 Subject: [PATCH 108/170] plugin to limit number of registered users --- plugins/UserLimitPlugin.php | 92 +++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 plugins/UserLimitPlugin.php diff --git a/plugins/UserLimitPlugin.php b/plugins/UserLimitPlugin.php new file mode 100644 index 0000000000..ab3187299e --- /dev/null +++ b/plugins/UserLimitPlugin.php @@ -0,0 +1,92 @@ +. + * + * @category Action + * @package StatusNet + * @author Evan Prodromou + * @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 limit number of users that can register (best for cloud providers) + * + * For cloud providers whose freemium model is based on how many + * users can register. We use it on the StatusNet Cloud. + * + * @category Plugin + * @package StatusNet + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + * + * @seeAlso Location + */ + +class UserLimitPlugin extends Plugin +{ + public $maxUsers = null; + + function onStartUserRegister(&$user, &$profile) + { + $this->_checkMaxUsers(); + return true; + } + + function onStartRegistrationTry($action) + { + $this->_checkMaxUsers(); + return true; + } + + function _checkMaxUsers() + { + if (!is_null($this->maxUsers)) { + + $cls = new User(); + + $cnt = $cls->count(); + + if ($cnt >= $this->maxUsers) { + $msg = sprintf(_('Cannot register; maximum number of users (%d) reached.'), + $this->maxUsers); + + throw new ClientException($msg); + } + } + } + + function onPluginVersion(&$versions) + { + $versions[] = array('name' => 'UserLimit', + 'version' => STATUSNET_VERSION, + 'author' => 'Evan Prodromou', + 'homepage' => 'http://status.net/wiki/Plugin:UserLimit', + 'description' => + _m('Limit the number of users who can register.')); + return true; + } +} From e765a9657b0491c6d4fd6436c9cd489342a465c9 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sat, 30 Jan 2010 18:45:10 +1300 Subject: [PATCH 109/170] move the schema DDL sql off into seperate files for each db we support --- lib/schema.mysql.php | 537 +++++++++++++++++++++++++++++++++++++++++++ lib/schema.pgsql.php | 504 ++++++++++++++++++++++++++++++++++++++++ lib/schema.php | 5 +- 3 files changed, 1045 insertions(+), 1 deletion(-) create mode 100644 lib/schema.mysql.php create mode 100644 lib/schema.pgsql.php diff --git a/lib/schema.mysql.php b/lib/schema.mysql.php new file mode 100644 index 0000000000..1f7c3d0926 --- /dev/null +++ b/lib/schema.mysql.php @@ -0,0 +1,537 @@ +. + * + * @category Database + * @package StatusNet + * @author Evan Prodromou + * @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); +} + +/** + * Class representing the database schema + * + * A class representing the database schema. Can be used to + * manipulate the schema -- especially for plugins and upgrade + * utilities. + * + * @category Database + * @package StatusNet + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +class MysqlSchema extends Schema +{ + static $_single = null; + protected $conn = null; + + /** + * Constructor. Only run once for singleton object. + */ + + protected function __construct() + { + // XXX: there should be an easier way to do this. + $user = new User(); + + $this->conn = $user->getDatabaseConnection(); + + $user->free(); + + unset($user); + } + + /** + * Main public entry point. Use this to get + * the singleton object. + * + * @return Schema the (single) Schema object + */ + + static function get() + { + if (empty(self::$_single)) { + self::$_single = new Schema(); + } + return self::$_single; + } + + /** + * Returns a TableDef object for the table + * in the schema with the given name. + * + * Throws an exception if the table is not found. + * + * @param string $name Name of the table to get + * + * @return TableDef tabledef for that table. + */ + + public function getTableDef($name) + { + $res = $this->conn->query('DESCRIBE ' . $name); + + if (PEAR::isError($res)) { + throw new Exception($res->getMessage()); + } + + $td = new TableDef(); + + $td->name = $name; + $td->columns = array(); + + $row = array(); + + while ($res->fetchInto($row, DB_FETCHMODE_ASSOC)) { + + $cd = new ColumnDef(); + + $cd->name = $row['Field']; + + $packed = $row['Type']; + + if (preg_match('/^(\w+)\((\d+)\)$/', $packed, $match)) { + $cd->type = $match[1]; + $cd->size = $match[2]; + } else { + $cd->type = $packed; + } + + $cd->nullable = ($row['Null'] == 'YES') ? true : false; + $cd->key = $row['Key']; + $cd->default = $row['Default']; + $cd->extra = $row['Extra']; + + $td->columns[] = $cd; + } + + return $td; + } + + /** + * Gets a ColumnDef object for a single column. + * + * Throws an exception if the table is not found. + * + * @param string $table name of the table + * @param string $column name of the column + * + * @return ColumnDef definition of the column or null + * if not found. + */ + + public function getColumnDef($table, $column) + { + $td = $this->getTableDef($table); + + foreach ($td->columns as $cd) { + if ($cd->name == $column) { + return $cd; + } + } + + return null; + } + + /** + * Creates a table with the given names and columns. + * + * @param string $name Name of the table + * @param array $columns Array of ColumnDef objects + * for new table. + * + * @return boolean success flag + */ + + public function createTable($name, $columns) + { + $uniques = array(); + $primary = array(); + $indices = array(); + + $sql = "CREATE TABLE $name (\n"; + + for ($i = 0; $i < count($columns); $i++) { + + $cd =& $columns[$i]; + + if ($i > 0) { + $sql .= ",\n"; + } + + $sql .= $this->_columnSql($cd); + + switch ($cd->key) { + case 'UNI': + $uniques[] = $cd->name; + break; + case 'PRI': + $primary[] = $cd->name; + break; + case 'MUL': + $indices[] = $cd->name; + break; + } + } + + if (count($primary) > 0) { // it really should be... + $sql .= ",\nconstraint primary key (" . implode(',', $primary) . ")"; + } + + foreach ($uniques as $u) { + $sql .= ",\nunique index {$name}_{$u}_idx ($u)"; + } + + foreach ($indices as $i) { + $sql .= ",\nindex {$name}_{$i}_idx ($i)"; + } + + $sql .= "); "; + + $res = $this->conn->query($sql); + + if (PEAR::isError($res)) { + throw new Exception($res->getMessage()); + } + + return true; + } + + /** + * Drops a table from the schema + * + * Throws an exception if the table is not found. + * + * @param string $name Name of the table to drop + * + * @return boolean success flag + */ + + public function dropTable($name) + { + $res = $this->conn->query("DROP TABLE $name"); + + if (PEAR::isError($res)) { + throw new Exception($res->getMessage()); + } + + return true; + } + + /** + * Adds an index to a table. + * + * If no name is provided, a name will be made up based + * on the table name and column names. + * + * Throws an exception on database error, esp. if the table + * does not exist. + * + * @param string $table Name of the table + * @param array $columnNames Name of columns to index + * @param string $name (Optional) name of the index + * + * @return boolean success flag + */ + + public function createIndex($table, $columnNames, $name=null) + { + if (!is_array($columnNames)) { + $columnNames = array($columnNames); + } + + if (empty($name)) { + $name = "$table_".implode("_", $columnNames)."_idx"; + } + + $res = $this->conn->query("ALTER TABLE $table ". + "ADD INDEX $name (". + implode(",", $columnNames).")"); + + if (PEAR::isError($res)) { + throw new Exception($res->getMessage()); + } + + return true; + } + + /** + * Drops a named index from a table. + * + * @param string $table name of the table the index is on. + * @param string $name name of the index + * + * @return boolean success flag + */ + + public function dropIndex($table, $name) + { + $res = $this->conn->query("ALTER TABLE $table DROP INDEX $name"); + + if (PEAR::isError($res)) { + throw new Exception($res->getMessage()); + } + + return true; + } + + /** + * Adds a column to a table + * + * @param string $table name of the table + * @param ColumnDef $columndef Definition of the new + * column. + * + * @return boolean success flag + */ + + public function addColumn($table, $columndef) + { + $sql = "ALTER TABLE $table ADD COLUMN " . $this->_columnSql($columndef); + + $res = $this->conn->query($sql); + + if (PEAR::isError($res)) { + throw new Exception($res->getMessage()); + } + + return true; + } + + /** + * Modifies a column in the schema. + * + * The name must match an existing column and table. + * + * @param string $table name of the table + * @param ColumnDef $columndef new definition of the column. + * + * @return boolean success flag + */ + + public function modifyColumn($table, $columndef) + { + $sql = "ALTER TABLE $table MODIFY COLUMN " . + $this->_columnSql($columndef); + + $res = $this->conn->query($sql); + + if (PEAR::isError($res)) { + throw new Exception($res->getMessage()); + } + + return true; + } + + /** + * Drops a column from a table + * + * The name must match an existing column. + * + * @param string $table name of the table + * @param string $columnName name of the column to drop + * + * @return boolean success flag + */ + + public function dropColumn($table, $columnName) + { + $sql = "ALTER TABLE $table DROP COLUMN $columnName"; + + $res = $this->conn->query($sql); + + if (PEAR::isError($res)) { + throw new Exception($res->getMessage()); + } + + return true; + } + + /** + * Ensures that a table exists with the given + * name and the given column definitions. + * + * If the table does not yet exist, it will + * create the table. If it does exist, it will + * alter the table to match the column definitions. + * + * @param string $tableName name of the table + * @param array $columns array of ColumnDef + * objects for the table + * + * @return boolean success flag + */ + + public function ensureTable($tableName, $columns) + { + // XXX: DB engine portability -> toilet + + try { + $td = $this->getTableDef($tableName); + } catch (Exception $e) { + if (preg_match('/no such table/', $e->getMessage())) { + return $this->createTable($tableName, $columns); + } else { + throw $e; + } + } + + $cur = $this->_names($td->columns); + $new = $this->_names($columns); + + $toadd = array_diff($new, $cur); + $todrop = array_diff($cur, $new); + $same = array_intersect($new, $cur); + $tomod = array(); + + foreach ($same as $m) { + $curCol = $this->_byName($td->columns, $m); + $newCol = $this->_byName($columns, $m); + + if (!$newCol->equals($curCol)) { + $tomod[] = $newCol->name; + } + } + + if (count($toadd) + count($todrop) + count($tomod) == 0) { + // nothing to do + return true; + } + + // For efficiency, we want this all in one + // query, instead of using our methods. + + $phrase = array(); + + foreach ($toadd as $columnName) { + $cd = $this->_byName($columns, $columnName); + + $phrase[] = 'ADD COLUMN ' . $this->_columnSql($cd); + } + + foreach ($todrop as $columnName) { + $phrase[] = 'DROP COLUMN ' . $columnName; + } + + foreach ($tomod as $columnName) { + $cd = $this->_byName($columns, $columnName); + + $phrase[] = 'MODIFY COLUMN ' . $this->_columnSql($cd); + } + + $sql = 'ALTER TABLE ' . $tableName . ' ' . implode(', ', $phrase); + + $res = $this->conn->query($sql); + + if (PEAR::isError($res)) { + throw new Exception($res->getMessage()); + } + + return true; + } + + /** + * Returns the array of names from an array of + * ColumnDef objects. + * + * @param array $cds array of ColumnDef objects + * + * @return array strings for name values + */ + + private function _names($cds) + { + $names = array(); + + foreach ($cds as $cd) { + $names[] = $cd->name; + } + + return $names; + } + + /** + * Get a ColumnDef from an array matching + * name. + * + * @param array $cds Array of ColumnDef objects + * @param string $name Name of the column + * + * @return ColumnDef matching item or null if no match. + */ + + private function _byName($cds, $name) + { + foreach ($cds as $cd) { + if ($cd->name == $name) { + return $cd; + } + } + + return null; + } + + /** + * Return the proper SQL for creating or + * altering a column. + * + * Appropriate for use in CREATE TABLE or + * ALTER TABLE statements. + * + * @param ColumnDef $cd column to create + * + * @return string correct SQL for that column + */ + + private function _columnSql($cd) + { + $sql = "{$cd->name} "; + + if (!empty($cd->size)) { + $sql .= "{$cd->type}({$cd->size}) "; + } else { + $sql .= "{$cd->type} "; + } + + if (!empty($cd->default)) { + $sql .= "default {$cd->default} "; + } else { + $sql .= ($cd->nullable) ? "null " : "not null "; + } + + if (!empty($cd->auto_increment)) { + $sql .= " auto_increment "; + } + + if (!empty($cd->extra)) { + $sql .= "{$cd->extra} "; + } + + return $sql; + } +} diff --git a/lib/schema.pgsql.php b/lib/schema.pgsql.php new file mode 100644 index 0000000000..7291106dc2 --- /dev/null +++ b/lib/schema.pgsql.php @@ -0,0 +1,504 @@ +. + * + * @category Database + * @package StatusNet + * @author Evan Prodromou + * @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); +} + +/** + * Class representing the database schema + * + * A class representing the database schema. Can be used to + * manipulate the schema -- especially for plugins and upgrade + * utilities. + * + * @category Database + * @package StatusNet + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +class PgsqlSchema extends Schema +{ + + /** + * Returns a TableDef object for the table + * in the schema with the given name. + * + * Throws an exception if the table is not found. + * + * @param string $name Name of the table to get + * + * @return TableDef tabledef for that table. + */ + + public function getTableDef($name) + { + $res = $this->conn->query('DESCRIBE ' . $name); + + if (PEAR::isError($res)) { + throw new Exception($res->getMessage()); + } + + $td = new TableDef(); + + $td->name = $name; + $td->columns = array(); + + $row = array(); + + while ($res->fetchInto($row, DB_FETCHMODE_ASSOC)) { + + $cd = new ColumnDef(); + + $cd->name = $row['Field']; + + $packed = $row['Type']; + + if (preg_match('/^(\w+)\((\d+)\)$/', $packed, $match)) { + $cd->type = $match[1]; + $cd->size = $match[2]; + } else { + $cd->type = $packed; + } + + $cd->nullable = ($row['Null'] == 'YES') ? true : false; + $cd->key = $row['Key']; + $cd->default = $row['Default']; + $cd->extra = $row['Extra']; + + $td->columns[] = $cd; + } + + return $td; + } + + /** + * Gets a ColumnDef object for a single column. + * + * Throws an exception if the table is not found. + * + * @param string $table name of the table + * @param string $column name of the column + * + * @return ColumnDef definition of the column or null + * if not found. + */ + + public function getColumnDef($table, $column) + { + $td = $this->getTableDef($table); + + foreach ($td->columns as $cd) { + if ($cd->name == $column) { + return $cd; + } + } + + return null; + } + + /** + * Creates a table with the given names and columns. + * + * @param string $name Name of the table + * @param array $columns Array of ColumnDef objects + * for new table. + * + * @return boolean success flag + */ + + public function createTable($name, $columns) + { + $uniques = array(); + $primary = array(); + $indices = array(); + + $sql = "CREATE TABLE $name (\n"; + + for ($i = 0; $i < count($columns); $i++) { + + $cd =& $columns[$i]; + + if ($i > 0) { + $sql .= ",\n"; + } + + $sql .= $this->_columnSql($cd); + + switch ($cd->key) { + case 'UNI': + $uniques[] = $cd->name; + break; + case 'PRI': + $primary[] = $cd->name; + break; + case 'MUL': + $indices[] = $cd->name; + break; + } + } + + if (count($primary) > 0) { // it really should be... + $sql .= ",\nconstraint primary key (" . implode(',', $primary) . ")"; + } + + foreach ($uniques as $u) { + $sql .= ",\nunique index {$name}_{$u}_idx ($u)"; + } + + foreach ($indices as $i) { + $sql .= ",\nindex {$name}_{$i}_idx ($i)"; + } + + $sql .= "); "; + + $res = $this->conn->query($sql); + + if (PEAR::isError($res)) { + throw new Exception($res->getMessage()); + } + + return true; + } + + /** + * Drops a table from the schema + * + * Throws an exception if the table is not found. + * + * @param string $name Name of the table to drop + * + * @return boolean success flag + */ + + public function dropTable($name) + { + $res = $this->conn->query("DROP TABLE $name"); + + if (PEAR::isError($res)) { + throw new Exception($res->getMessage()); + } + + return true; + } + + /** + * Adds an index to a table. + * + * If no name is provided, a name will be made up based + * on the table name and column names. + * + * Throws an exception on database error, esp. if the table + * does not exist. + * + * @param string $table Name of the table + * @param array $columnNames Name of columns to index + * @param string $name (Optional) name of the index + * + * @return boolean success flag + */ + + public function createIndex($table, $columnNames, $name=null) + { + if (!is_array($columnNames)) { + $columnNames = array($columnNames); + } + + if (empty($name)) { + $name = "$table_".implode("_", $columnNames)."_idx"; + } + + $res = $this->conn->query("ALTER TABLE $table ". + "ADD INDEX $name (". + implode(",", $columnNames).")"); + + if (PEAR::isError($res)) { + throw new Exception($res->getMessage()); + } + + return true; + } + + /** + * Drops a named index from a table. + * + * @param string $table name of the table the index is on. + * @param string $name name of the index + * + * @return boolean success flag + */ + + public function dropIndex($table, $name) + { + $res = $this->conn->query("ALTER TABLE $table DROP INDEX $name"); + + if (PEAR::isError($res)) { + throw new Exception($res->getMessage()); + } + + return true; + } + + /** + * Adds a column to a table + * + * @param string $table name of the table + * @param ColumnDef $columndef Definition of the new + * column. + * + * @return boolean success flag + */ + + public function addColumn($table, $columndef) + { + $sql = "ALTER TABLE $table ADD COLUMN " . $this->_columnSql($columndef); + + $res = $this->conn->query($sql); + + if (PEAR::isError($res)) { + throw new Exception($res->getMessage()); + } + + return true; + } + + /** + * Modifies a column in the schema. + * + * The name must match an existing column and table. + * + * @param string $table name of the table + * @param ColumnDef $columndef new definition of the column. + * + * @return boolean success flag + */ + + public function modifyColumn($table, $columndef) + { + $sql = "ALTER TABLE $table MODIFY COLUMN " . + $this->_columnSql($columndef); + + $res = $this->conn->query($sql); + + if (PEAR::isError($res)) { + throw new Exception($res->getMessage()); + } + + return true; + } + + /** + * Drops a column from a table + * + * The name must match an existing column. + * + * @param string $table name of the table + * @param string $columnName name of the column to drop + * + * @return boolean success flag + */ + + public function dropColumn($table, $columnName) + { + $sql = "ALTER TABLE $table DROP COLUMN $columnName"; + + $res = $this->conn->query($sql); + + if (PEAR::isError($res)) { + throw new Exception($res->getMessage()); + } + + return true; + } + + /** + * Ensures that a table exists with the given + * name and the given column definitions. + * + * If the table does not yet exist, it will + * create the table. If it does exist, it will + * alter the table to match the column definitions. + * + * @param string $tableName name of the table + * @param array $columns array of ColumnDef + * objects for the table + * + * @return boolean success flag + */ + + public function ensureTable($tableName, $columns) + { + // XXX: DB engine portability -> toilet + + try { + $td = $this->getTableDef($tableName); + } catch (Exception $e) { + if (preg_match('/no such table/', $e->getMessage())) { + return $this->createTable($tableName, $columns); + } else { + throw $e; + } + } + + $cur = $this->_names($td->columns); + $new = $this->_names($columns); + + $toadd = array_diff($new, $cur); + $todrop = array_diff($cur, $new); + $same = array_intersect($new, $cur); + $tomod = array(); + + foreach ($same as $m) { + $curCol = $this->_byName($td->columns, $m); + $newCol = $this->_byName($columns, $m); + + if (!$newCol->equals($curCol)) { + $tomod[] = $newCol->name; + } + } + + if (count($toadd) + count($todrop) + count($tomod) == 0) { + // nothing to do + return true; + } + + // For efficiency, we want this all in one + // query, instead of using our methods. + + $phrase = array(); + + foreach ($toadd as $columnName) { + $cd = $this->_byName($columns, $columnName); + + $phrase[] = 'ADD COLUMN ' . $this->_columnSql($cd); + } + + foreach ($todrop as $columnName) { + $phrase[] = 'DROP COLUMN ' . $columnName; + } + + foreach ($tomod as $columnName) { + $cd = $this->_byName($columns, $columnName); + + $phrase[] = 'MODIFY COLUMN ' . $this->_columnSql($cd); + } + + $sql = 'ALTER TABLE ' . $tableName . ' ' . implode(', ', $phrase); + + $res = $this->conn->query($sql); + + if (PEAR::isError($res)) { + throw new Exception($res->getMessage()); + } + + return true; + } + + /** + * Returns the array of names from an array of + * ColumnDef objects. + * + * @param array $cds array of ColumnDef objects + * + * @return array strings for name values + */ + + private function _names($cds) + { + $names = array(); + + foreach ($cds as $cd) { + $names[] = $cd->name; + } + + return $names; + } + + /** + * Get a ColumnDef from an array matching + * name. + * + * @param array $cds Array of ColumnDef objects + * @param string $name Name of the column + * + * @return ColumnDef matching item or null if no match. + */ + + private function _byName($cds, $name) + { + foreach ($cds as $cd) { + if ($cd->name == $name) { + return $cd; + } + } + + return null; + } + + /** + * Return the proper SQL for creating or + * altering a column. + * + * Appropriate for use in CREATE TABLE or + * ALTER TABLE statements. + * + * @param ColumnDef $cd column to create + * + * @return string correct SQL for that column + */ + + private function _columnSql($cd) + { + $sql = "{$cd->name} "; + + if (!empty($cd->size)) { + $sql .= "{$cd->type}({$cd->size}) "; + } else { + $sql .= "{$cd->type} "; + } + + if (!empty($cd->default)) { + $sql .= "default {$cd->default} "; + } else { + $sql .= ($cd->nullable) ? "null " : "not null "; + } + + if (!empty($cd->auto_increment)) { + $sql .= " auto_increment "; + } + + if (!empty($cd->extra)) { + $sql .= "{$cd->extra} "; + } + + return $sql; + } +} diff --git a/lib/schema.php b/lib/schema.php index a7f64ebed1..164387ff3d 100644 --- a/lib/schema.php +++ b/lib/schema.php @@ -75,8 +75,11 @@ class Schema static function get() { + $type = common_config('db', 'type'); if (empty(self::$_single)) { - self::$_single = new Schema(); + include "lib/schema.{$type}.php"; + $class = $type.='Schema'; + self::$_single = new $class(); } return self::$_single; } From 870c83c17de9710800163570cdc5321591e73c34 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sun, 10 Jan 2010 05:21:23 +0000 Subject: [PATCH 110/170] getTableDef() mostly working in postgres --- lib/schema.php | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/lib/schema.php b/lib/schema.php index 164387ff3d..c1636c21d8 100644 --- a/lib/schema.php +++ b/lib/schema.php @@ -97,7 +97,12 @@ class Schema public function getTableDef($name) { - $res = $this->conn->query('DESCRIBE ' . $name); + if(common_config('db','type') == 'pgsql') { + $res = $this->conn->query("select column_default as default, is_nullable as Null, udt_name as Type, column_name AS Field from INFORMATION_SCHEMA.COLUMNS where table_name = '$name'"); + } + else { + $res = $this->conn->query('DESCRIBE ' . $name); + } if (PEAR::isError($res)) { throw new Exception($res->getMessage()); @@ -111,12 +116,16 @@ class Schema $row = array(); while ($res->fetchInto($row, DB_FETCHMODE_ASSOC)) { + //lower case the keys, because the php postgres driver is case insentive for column names + foreach($row as $k=>$v) { + $row[strtolower($k)] = $row[$k]; + } $cd = new ColumnDef(); - $cd->name = $row['Field']; + $cd->name = $row['field']; - $packed = $row['Type']; + $packed = $row['type']; if (preg_match('/^(\w+)\((\d+)\)$/', $packed, $match)) { $cd->type = $match[1]; @@ -125,9 +134,9 @@ class Schema $cd->type = $packed; } - $cd->nullable = ($row['Null'] == 'YES') ? true : false; + $cd->nullable = ($row['null'] == 'YES') ? true : false; $cd->key = $row['Key']; - $cd->default = $row['Default']; + $cd->default = $row['default']; $cd->extra = $row['Extra']; $td->columns[] = $cd; From 22a6e46b45226647b6e7b9bfdc013e59c4a52428 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sat, 30 Jan 2010 21:22:30 +1300 Subject: [PATCH 111/170] removed describeTable from base class, and fixed it up in pgsql --- lib/schema.pgsql.php | 13 +++++----- lib/schema.php | 60 -------------------------------------------- 2 files changed, 6 insertions(+), 67 deletions(-) diff --git a/lib/schema.pgsql.php b/lib/schema.pgsql.php index 7291106dc2..91bc09667c 100644 --- a/lib/schema.pgsql.php +++ b/lib/schema.pgsql.php @@ -61,7 +61,7 @@ class PgsqlSchema extends Schema public function getTableDef($name) { - $res = $this->conn->query('DESCRIBE ' . $name); + $res = $this->conn->query("select *, column_default as default, is_nullable as Null, udt_name as Type, column_name AS Field from INFORMATION_SCHEMA.COLUMNS where table_name = '$name'"); if (PEAR::isError($res)) { throw new Exception($res->getMessage()); @@ -75,12 +75,12 @@ class PgsqlSchema extends Schema $row = array(); while ($res->fetchInto($row, DB_FETCHMODE_ASSOC)) { - +// var_dump($row); $cd = new ColumnDef(); - $cd->name = $row['Field']; + $cd->name = $row['field']; - $packed = $row['Type']; + $packed = $row['type']; if (preg_match('/^(\w+)\((\d+)\)$/', $packed, $match)) { $cd->type = $match[1]; @@ -89,14 +89,13 @@ class PgsqlSchema extends Schema $cd->type = $packed; } - $cd->nullable = ($row['Null'] == 'YES') ? true : false; + $cd->nullable = ($row['null'] == 'YES') ? true : false; $cd->key = $row['Key']; - $cd->default = $row['Default']; + $cd->default = $row['default']; $cd->extra = $row['Extra']; $td->columns[] = $cd; } - return $td; } diff --git a/lib/schema.php b/lib/schema.php index c1636c21d8..27a4deda11 100644 --- a/lib/schema.php +++ b/lib/schema.php @@ -84,66 +84,6 @@ class Schema return self::$_single; } - /** - * Returns a TableDef object for the table - * in the schema with the given name. - * - * Throws an exception if the table is not found. - * - * @param string $name Name of the table to get - * - * @return TableDef tabledef for that table. - */ - - public function getTableDef($name) - { - if(common_config('db','type') == 'pgsql') { - $res = $this->conn->query("select column_default as default, is_nullable as Null, udt_name as Type, column_name AS Field from INFORMATION_SCHEMA.COLUMNS where table_name = '$name'"); - } - else { - $res = $this->conn->query('DESCRIBE ' . $name); - } - - if (PEAR::isError($res)) { - throw new Exception($res->getMessage()); - } - - $td = new TableDef(); - - $td->name = $name; - $td->columns = array(); - - $row = array(); - - while ($res->fetchInto($row, DB_FETCHMODE_ASSOC)) { - //lower case the keys, because the php postgres driver is case insentive for column names - foreach($row as $k=>$v) { - $row[strtolower($k)] = $row[$k]; - } - - $cd = new ColumnDef(); - - $cd->name = $row['field']; - - $packed = $row['type']; - - if (preg_match('/^(\w+)\((\d+)\)$/', $packed, $match)) { - $cd->type = $match[1]; - $cd->size = $match[2]; - } else { - $cd->type = $packed; - } - - $cd->nullable = ($row['null'] == 'YES') ? true : false; - $cd->key = $row['Key']; - $cd->default = $row['default']; - $cd->extra = $row['Extra']; - - $td->columns[] = $cd; - } - - return $td; - } /** * Gets a ColumnDef object for a single column. From bb0bf635d881e4238cfce3cbb269b3f70b45b8e3 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Sat, 30 Jan 2010 15:03:01 +0100 Subject: [PATCH 112/170] Using form object instead of form_id and find(). Slightly faster and easier to read. --- js/util.js | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/js/util.js b/js/util.js index b864867fd1..6bc174049a 100644 --- a/js/util.js +++ b/js/util.js @@ -185,13 +185,13 @@ var SN = { // StatusNet dataType: 'xml', timeout: '60000', beforeSend: function(formData) { - if ($('#'+form_id+' #'+SN.C.S.NoticeDataText)[0].value.length === 0) { + if (form.find('#'+SN.C.S.NoticeDataText)[0].value.length === 0) { form.addClass(SN.C.S.Warning); return false; } form.addClass(SN.C.S.Processing); - $('#'+form_id+' #'+SN.C.S.NoticeActionSubmit).addClass(SN.C.S.Disabled); - $('#'+form_id+' #'+SN.C.S.NoticeActionSubmit).attr(SN.C.S.Disabled, SN.C.S.Disabled); + form.find('#'+SN.C.S.NoticeActionSubmit).addClass(SN.C.S.Disabled); + form.find('#'+SN.C.S.NoticeActionSubmit).attr(SN.C.S.Disabled, SN.C.S.Disabled); NLat = $('#'+SN.C.S.NoticeLat).val(); NLon = $('#'+SN.C.S.NoticeLon).val(); @@ -221,9 +221,9 @@ var SN = { // StatusNet }, error: function (xhr, textStatus, errorThrown) { form.removeClass(SN.C.S.Processing); - $('#'+form_id+' #'+SN.C.S.NoticeActionSubmit).removeClass(SN.C.S.Disabled); - $('#'+form_id+' #'+SN.C.S.NoticeActionSubmit).removeAttr(SN.C.S.Disabled, SN.C.S.Disabled); - $('#'+form_id+' .form_response').remove(); + form.find('#'+SN.C.S.NoticeActionSubmit).removeClass(SN.C.S.Disabled); + form.find('#'+SN.C.S.NoticeActionSubmit).removeAttr(SN.C.S.Disabled, SN.C.S.Disabled); + form.find('.form_response').remove(); if (textStatus == 'timeout') { form.append('

    Sorry! We had trouble sending your notice. The servers are overloaded. Please try again, and contact the site administrator if this problem persists.

    '); } @@ -233,9 +233,9 @@ var SN = { // StatusNet } else { if (parseInt(xhr.status) === 0 || jQuery.inArray(parseInt(xhr.status), SN.C.I.HTTP20x30x) >= 0) { - $('#'+form_id).resetForm(); - $('#'+form_id+' #'+SN.C.S.NoticeDataAttachSelected).remove(); - SN.U.FormNoticeEnhancements($('#'+form_id)); + form.resetForm(); + form.find('#'+SN.C.S.NoticeDataAttachSelected).remove(); + SN.U.FormNoticeEnhancements(form); } else { form.append('

    (Sorry! We had trouble sending your notice ('+xhr.status+' '+xhr.statusText+'). Please report the problem to the site administrator if this happens again.

    '); @@ -244,7 +244,7 @@ var SN = { // StatusNet } }, success: function(data, textStatus) { - $('#'+form_id+' .form_response').remove(); + form.find('.form_response').remove(); var result; if ($('#'+SN.C.S.Error, data).length > 0) { result = document._importNode($('p', data)[0], true); @@ -290,16 +290,16 @@ var SN = { // StatusNet form.append('

    '+result_title+'

    '); } } - $('#'+form_id).resetForm(); - $('#'+form_id+' #'+SN.C.S.NoticeInReplyTo).val(''); - $('#'+form_id+' #'+SN.C.S.NoticeDataAttachSelected).remove(); - SN.U.FormNoticeEnhancements($('#'+form_id)); + form.resetForm(); + form.find('#'+SN.C.S.NoticeInReplyTo).val(''); + form.find('#'+SN.C.S.NoticeDataAttachSelected).remove(); + SN.U.FormNoticeEnhancements(form); } }, complete: function(xhr, textStatus) { form.removeClass(SN.C.S.Processing); - $('#'+form_id+' #'+SN.C.S.NoticeActionSubmit).removeAttr(SN.C.S.Disabled); - $('#'+form_id+' #'+SN.C.S.NoticeActionSubmit).removeClass(SN.C.S.Disabled); + form.find('#'+SN.C.S.NoticeActionSubmit).removeAttr(SN.C.S.Disabled); + form.find('#'+SN.C.S.NoticeActionSubmit).removeClass(SN.C.S.Disabled); $('#'+SN.C.S.NoticeLat).val(NLat); $('#'+SN.C.S.NoticeLon).val(NLon); From aa3170cf34e2b2120547691e204d5ac3bdfd07aa Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Sat, 30 Jan 2010 15:19:13 +0100 Subject: [PATCH 113/170] Using jQuery chaining in FormNoticeXHR --- js/util.js | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/js/util.js b/js/util.js index 6bc174049a..4818be47e0 100644 --- a/js/util.js +++ b/js/util.js @@ -189,9 +189,11 @@ var SN = { // StatusNet form.addClass(SN.C.S.Warning); return false; } - form.addClass(SN.C.S.Processing); - form.find('#'+SN.C.S.NoticeActionSubmit).addClass(SN.C.S.Disabled); - form.find('#'+SN.C.S.NoticeActionSubmit).attr(SN.C.S.Disabled, SN.C.S.Disabled); + form + .addClass(SN.C.S.Processing) + .find('#'+SN.C.S.NoticeActionSubmit) + .addClass(SN.C.S.Disabled) + .attr(SN.C.S.Disabled, SN.C.S.Disabled); NLat = $('#'+SN.C.S.NoticeLat).val(); NLon = $('#'+SN.C.S.NoticeLon).val(); @@ -220,9 +222,11 @@ var SN = { // StatusNet return true; }, error: function (xhr, textStatus, errorThrown) { - form.removeClass(SN.C.S.Processing); - form.find('#'+SN.C.S.NoticeActionSubmit).removeClass(SN.C.S.Disabled); - form.find('#'+SN.C.S.NoticeActionSubmit).removeAttr(SN.C.S.Disabled, SN.C.S.Disabled); + form + .removeClass(SN.C.S.Processing) + .find('#'+SN.C.S.NoticeActionSubmit) + .removeClass(SN.C.S.Disabled) + .removeAttr(SN.C.S.Disabled, SN.C.S.Disabled); form.find('.form_response').remove(); if (textStatus == 'timeout') { form.append('

    Sorry! We had trouble sending your notice. The servers are overloaded. Please try again, and contact the site administrator if this problem persists.

    '); @@ -233,8 +237,9 @@ var SN = { // StatusNet } else { if (parseInt(xhr.status) === 0 || jQuery.inArray(parseInt(xhr.status), SN.C.I.HTTP20x30x) >= 0) { - form.resetForm(); - form.find('#'+SN.C.S.NoticeDataAttachSelected).remove(); + form + .resetForm() + .find('#'+SN.C.S.NoticeDataAttachSelected).remove(); SN.U.FormNoticeEnhancements(form); } else { @@ -277,8 +282,9 @@ var SN = { // StatusNet else { notices.prepend(notice); } - $('#'+notice.id).css({display:'none'}); - $('#'+notice.id).fadeIn(2500); + $('#'+notice.id) + .css({display:'none'}) + .fadeIn(2500); SN.U.NoticeWithAttachment($('#'+notice.id)); SN.U.NoticeReplyTo($('#'+notice.id)); SN.U.FormXHR($('#'+notice.id+' .form_favor')); @@ -297,9 +303,11 @@ var SN = { // StatusNet } }, complete: function(xhr, textStatus) { - form.removeClass(SN.C.S.Processing); - form.find('#'+SN.C.S.NoticeActionSubmit).removeAttr(SN.C.S.Disabled); - form.find('#'+SN.C.S.NoticeActionSubmit).removeClass(SN.C.S.Disabled); + form + .removeClass(SN.C.S.Processing) + .find('#'+SN.C.S.NoticeActionSubmit) + .removeAttr(SN.C.S.Disabled) + .removeClass(SN.C.S.Disabled); $('#'+SN.C.S.NoticeLat).val(NLat); $('#'+SN.C.S.NoticeLon).val(NLon); From 4d0ee6a41f3cfb2e99dcb92e7b14f3183393f1a5 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Sat, 30 Jan 2010 15:28:31 +0100 Subject: [PATCH 114/170] Globalized form notice data geo values --- js/util.js | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/js/util.js b/js/util.js index 4818be47e0..bbc6fdb288 100644 --- a/js/util.js +++ b/js/util.js @@ -178,7 +178,7 @@ var SN = { // StatusNet }, FormNoticeXHR: function(form) { - var NDG, NLat, NLon, NLNS, NLID; + SN.C.I.NoticeDataGeo = {}; form_id = form.attr('id'); form.append(''); form.ajaxForm({ @@ -195,28 +195,28 @@ var SN = { // StatusNet .addClass(SN.C.S.Disabled) .attr(SN.C.S.Disabled, SN.C.S.Disabled); - NLat = $('#'+SN.C.S.NoticeLat).val(); - NLon = $('#'+SN.C.S.NoticeLon).val(); - NLNS = $('#'+SN.C.S.NoticeLocationNs).val(); - NLID = $('#'+SN.C.S.NoticeLocationId).val(); - NDG = $('#'+SN.C.S.NoticeDataGeo).attr('checked'); + SN.C.I.NoticeDataGeo.NLat = $('#'+SN.C.S.NoticeLat).val(); + SN.C.I.NoticeDataGeo.NLon = $('#'+SN.C.S.NoticeLon).val(); + SN.C.I.NoticeDataGeo.NLNS = $('#'+SN.C.S.NoticeLocationNs).val(); + SN.C.I.NoticeDataGeo.NLID = $('#'+SN.C.S.NoticeLocationId).val(); + SN.C.I.NoticeDataGeo.NDG = $('#'+SN.C.S.NoticeDataGeo).attr('checked'); cookieValue = $.cookie(SN.C.S.NoticeDataGeoCookie); if (cookieValue !== null && cookieValue != 'disabled') { cookieValue = JSON.parse(cookieValue); - NLat = $('#'+SN.C.S.NoticeLat).val(cookieValue.NLat).val(); - NLon = $('#'+SN.C.S.NoticeLon).val(cookieValue.NLon).val(); + SN.C.I.NoticeDataGeo.NLat = $('#'+SN.C.S.NoticeLat).val(cookieValue.NLat).val(); + SN.C.I.NoticeDataGeo.NLon = $('#'+SN.C.S.NoticeLon).val(cookieValue.NLon).val(); if ($('#'+SN.C.S.NoticeLocationNs).val(cookieValue.NLNS)) { - NLNS = $('#'+SN.C.S.NoticeLocationNs).val(cookieValue.NLNS).val(); - NLID = $('#'+SN.C.S.NoticeLocationId).val(cookieValue.NLID).val(); + SN.C.I.NoticeDataGeo.NLNS = $('#'+SN.C.S.NoticeLocationNs).val(cookieValue.NLNS).val(); + SN.C.I.NoticeDataGeo.NLID = $('#'+SN.C.S.NoticeLocationId).val(cookieValue.NLID).val(); } } if (cookieValue == 'disabled') { - NDG = $('#'+SN.C.S.NoticeDataGeo).attr('checked', false).attr('checked'); + SN.C.I.NoticeDataGeo.NDG = $('#'+SN.C.S.NoticeDataGeo).attr('checked', false).attr('checked'); } else { - NDG = $('#'+SN.C.S.NoticeDataGeo).attr('checked', true).attr('checked'); + SN.C.I.NoticeDataGeo.NDG = $('#'+SN.C.S.NoticeDataGeo).attr('checked', true).attr('checked'); } return true; @@ -309,13 +309,13 @@ var SN = { // StatusNet .removeAttr(SN.C.S.Disabled) .removeClass(SN.C.S.Disabled); - $('#'+SN.C.S.NoticeLat).val(NLat); - $('#'+SN.C.S.NoticeLon).val(NLon); + $('#'+SN.C.S.NoticeLat).val(SN.C.I.NoticeDataGeo.NLat); + $('#'+SN.C.S.NoticeLon).val(SN.C.I.NoticeDataGeo.NLon); if ($('#'+SN.C.S.NoticeLocationNs)) { - $('#'+SN.C.S.NoticeLocationNs).val(NLNS); - $('#'+SN.C.S.NoticeLocationId).val(NLID); + $('#'+SN.C.S.NoticeLocationNs).val(SN.C.I.NoticeDataGeo.NLNS); + $('#'+SN.C.S.NoticeLocationId).val(SN.C.I.NoticeDataGeo.NLID); } - $('#'+SN.C.S.NoticeDataGeo).attr('checked', NDG); + $('#'+SN.C.S.NoticeDataGeo).attr('checked', SN.C.I.NoticeDataGeo.NDG); } }); }, From a1c9874a6119774a16917a631a7bb63f73a16ba1 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sat, 30 Jan 2010 12:40:11 -0500 Subject: [PATCH 115/170] better handling of null responses from geonames.org --- plugins/GeonamesPlugin.php | 78 ++++++++++++++++++++++++++++++-------- 1 file changed, 62 insertions(+), 16 deletions(-) diff --git a/plugins/GeonamesPlugin.php b/plugins/GeonamesPlugin.php index 52cc9c97f9..589462ed99 100644 --- a/plugins/GeonamesPlugin.php +++ b/plugins/GeonamesPlugin.php @@ -71,7 +71,7 @@ class GeonamesPlugin extends Plugin $loc = $this->getCache(array('name' => $name, 'language' => $language)); - if (!empty($loc)) { + if ($loc !== false) { $location = $loc; return false; } @@ -87,12 +87,20 @@ class GeonamesPlugin extends Plugin return true; } + if (count($geonames) == 0) { + // no results + $this->setCache(array('name' => $name, + 'language' => $language), + null); + return true; + } + $n = $geonames[0]; $location = new Location(); - $location->lat = (string)$n->lat; - $location->lon = (string)$n->lng; + $location->lat = $this->canonical($n->lat); + $location->lon = $this->canonical($n->lng); $location->names[$language] = (string)$n->name; $location->location_id = (string)$n->geonameId; $location->location_ns = self::LOCATION_NS; @@ -125,7 +133,7 @@ class GeonamesPlugin extends Plugin $loc = $this->getCache(array('id' => $id)); - if (!empty($loc)) { + if ($loc !== false) { $location = $loc; return false; } @@ -157,8 +165,9 @@ class GeonamesPlugin extends Plugin $location->location_id = (string)$last->geonameId; $location->location_ns = self::LOCATION_NS; - $location->lat = (string)$last->lat; - $location->lon = (string)$last->lng; + $location->lat = $this->canonical($last->lat); + $location->lon = $this->canonical($last->lng); + $location->names[$language] = implode(', ', array_reverse($parts)); $this->setCache(array('id' => (string)$last->geonameId), @@ -186,13 +195,15 @@ class GeonamesPlugin extends Plugin function onLocationFromLatLon($lat, $lon, $language, &$location) { - $lat = rtrim($lat, "0"); - $lon = rtrim($lon, "0"); + // Make sure they're canonical + + $lat = $this->canonical($lat); + $lon = $this->canonical($lon); $loc = $this->getCache(array('lat' => $lat, 'lon' => $lon)); - if (!empty($loc)) { + if ($loc !== false) { $location = $loc; return false; } @@ -207,6 +218,14 @@ class GeonamesPlugin extends Plugin return true; } + if (count($geonames) == 0) { + // no results + $this->setCache(array('lat' => $lat, + 'lon' => $lon), + null); + return true; + } + $n = $geonames[0]; $parts = array(); @@ -225,8 +244,8 @@ class GeonamesPlugin extends Plugin $location->location_id = (string)$n->geonameId; $location->location_ns = self::LOCATION_NS; - $location->lat = (string)$lat; - $location->lon = (string)$lon; + $location->lat = $this->canonical($n->lat); + $location->lon = $this->canonical($n->lng); $location->names[$language] = implode(', ', $parts); @@ -264,7 +283,7 @@ class GeonamesPlugin extends Plugin $n = $this->getCache(array('id' => $id, 'language' => $language)); - if (!empty($n)) { + if ($n !== false) { $name = $n; return false; } @@ -278,6 +297,13 @@ class GeonamesPlugin extends Plugin return false; } + if (count($geonames) == 0) { + $this->setCache(array('id' => $id, + 'language' => $language), + null); + return false; + } + $parts = array(); foreach ($geonames as $level) { @@ -412,17 +438,29 @@ class GeonamesPlugin extends Plugin throw new Exception("HTTP error code " . $result->code); } - $document = new SimpleXMLElement($result->getBody()); + $body = $result->getBody(); - if (empty($document)) { - throw new Exception("No results in response"); + if (empty($body)) { + throw new Exception("Empty HTTP body in response"); + } + + // This will throw an exception if the XML is mal-formed + + $document = new SimpleXMLElement($body); + + // No children, usually no results + + $children = $document->children(); + + if (count($children) == 0) { + return array(); } if (isset($document->status)) { throw new Exception("Error #".$document->status['value']." ('".$document->status['message']."')"); } - // Array of elements + // Array of elements, >0 elements return $document->geoname; } @@ -438,4 +476,12 @@ class GeonamesPlugin extends Plugin 'names for locations based on user-provided lat/long pairs.')); return true; } + + function canonical($coord) + { + $coord = rtrim($coord, "0"); + $coord = rtrim($coord, "."); + + return $coord; + } } From 1b7cc3393a6a039d4adcf955b3a79fe3c5cc6f47 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sat, 30 Jan 2010 12:43:00 -0500 Subject: [PATCH 116/170] Use passed-in lat long in geocode.php Don't rewrite the lat-long for a location in geocode.php. --- actions/geocode.php | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/actions/geocode.php b/actions/geocode.php index 9671d2c276..7fd696baf9 100644 --- a/actions/geocode.php +++ b/actions/geocode.php @@ -52,12 +52,7 @@ class GeocodeAction extends Action } $this->lat = $this->trimmed('lat'); $this->lon = $this->trimmed('lon'); - $location = Location::fromLatLon($this->lat, $this->lon); - if ($location) { - $this->location = Location::fromId($location->location_id, $location->location_ns); - $this->lat = $this->location->lat; - $this->lon = $this->location->lon; - } + $this->location = Location::fromLatLon($this->lat, $this->lon); return true; } From def5d56ce1582c785cbff7d49103493b293838bb Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sat, 30 Jan 2010 12:47:21 -0500 Subject: [PATCH 117/170] add lat, lon, location and remove closing tag from geocode.php --- actions/geocode.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/actions/geocode.php b/actions/geocode.php index 7fd696baf9..e883c6ce41 100644 --- a/actions/geocode.php +++ b/actions/geocode.php @@ -42,6 +42,10 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { */ class GeocodeAction extends Action { + var $lat = null; + var $lon = null; + var $location = null; + function prepare($args) { parent::prepare($args); @@ -90,4 +94,3 @@ class GeocodeAction extends Action return true; } } -?> From 4ae31f3476f30cf397c0d768119be3638be6fbf0 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sat, 30 Jan 2010 13:15:17 -0500 Subject: [PATCH 118/170] on exceptions, stomp logs the error and reenqueues --- lib/stompqueuemanager.php | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/lib/stompqueuemanager.php b/lib/stompqueuemanager.php index ec150bbb6e..6730cd213d 100644 --- a/lib/stompqueuemanager.php +++ b/lib/stompqueuemanager.php @@ -31,7 +31,6 @@ require_once 'Stomp.php'; require_once 'Stomp/Exception.php'; - class StompQueueManager extends QueueManager { protected $servers; @@ -41,7 +40,7 @@ class StompQueueManager extends QueueManager protected $control; protected $useTransactions = true; - + protected $sites = array(); protected $subscriptions = array(); @@ -182,7 +181,7 @@ class StompQueueManager extends QueueManager $this->_connect(); return $this->_doEnqueue($object, $queue, $this->defaultIdx); } - + /** * Saves a notice object reference into the queue item table * on the given connection. @@ -354,7 +353,7 @@ class StompQueueManager extends QueueManager } return true; } - + /** * Subscribe to all the queues we're going to need to handle... * @@ -459,7 +458,7 @@ class StompQueueManager extends QueueManager if ($con) { $this->cons[$idx] = $con; $this->disconnect[$idx] = null; - + // now we have to listen to everything... // @fixme refactor this nicer. :P $host = $con->getServer(); @@ -587,7 +586,15 @@ class StompQueueManager extends QueueManager return false; } - $ok = $handler->handle($item); + // If there's an exception when handling, + // log the error and let it get requeued. + + try { + $ok = $handler->handle($item); + } catch (Exception $e) { + $this->_log(LOG_ERR, "Exception on queue $queue: " . $e->getMessage()); + $ok = false; + } if (!$ok) { $this->_log(LOG_WARNING, "Failed handling $info"); @@ -646,7 +653,7 @@ class StompQueueManager extends QueueManager $this->begin($idx); return $shutdown; } - + /** * Set us up with queue subscriptions for a new site added at runtime, * triggered by a broadcast to the 'statusnet-control' topic. From fec8066bf76948142828b689708386861d089fb3 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sat, 30 Jan 2010 14:37:39 -0500 Subject: [PATCH 119/170] error clearing tags for profiles from memcached --- classes/Notice.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/Notice.php b/classes/Notice.php index a60dd5bcd7..42878d94f1 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -140,7 +140,7 @@ class Notice extends Memcached_DataObject foreach(array_unique($hashtags) as $hashtag) { /* elide characters we don't want in the tag */ $this->saveTag($hashtag); - self::blow('profile:notice_ids_tagged:%d:%s', $this->profile_id, $tag->tag); + self::blow('profile:notice_ids_tagged:%d:%s', $this->profile_id, $hashtag); } return true; } From dc62246443e3584ef5267505275f618f6fa86bf7 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sun, 31 Jan 2010 10:12:26 -0500 Subject: [PATCH 120/170] Add a robots.txt URL to the site root Adds a robots.txt file to the site root. Defaults defined by 'robotstxt' section of config. New events StartRobotsTxt and EndRobotsTxt to let plugins add information. Probably not useful if path is not /, but won't hurt anything, either. --- EVENTS.txt | 6 +++ README | 14 ++++++ actions/robotstxt.php | 100 ++++++++++++++++++++++++++++++++++++++++++ index.php | 5 ++- lib/default.php | 4 ++ lib/router.php | 2 + 6 files changed, 129 insertions(+), 2 deletions(-) create mode 100644 actions/robotstxt.php diff --git a/EVENTS.txt b/EVENTS.txt index 3317c80dec..6bf12bf13f 100644 --- a/EVENTS.txt +++ b/EVENTS.txt @@ -708,3 +708,9 @@ EndUserRegister: When a new user has been registered - &$profile: new profile data - &$user: new user account +StartRobotsTxt: Before outputting the robots.txt page +- &$action: RobotstxtAction being shown + +EndRobotsTxt: After the default robots.txt page (good place for customization) +- &$action: RobotstxtAction being shown + diff --git a/README b/README index da278f7412..4e576dcdd3 100644 --- a/README +++ b/README @@ -1496,6 +1496,20 @@ interface. It also makes the user's profile the root URL. enabled: Whether to run in "single user mode". Default false. nickname: nickname of the single user. +robotstxt +--------- + +We put out a default robots.txt file to guide the processing of +Web crawlers. See http://www.robotstxt.org/ for more information +on the format of this file. + +crawldelay: if non-empty, this value is provided as the Crawl-Delay: + for the robots.txt file. see http://ur1.ca/l5a0 + for more information. Default is zero, no explicit delay. +disallow: Array of (virtual) directories to disallow. Default is 'main', + 'search', 'message', 'settings', 'admin'. Ignored when site + is private, in which case the entire site ('/') is disallowed. + Plugins ======= diff --git a/actions/robotstxt.php b/actions/robotstxt.php new file mode 100644 index 0000000000..5131097c8c --- /dev/null +++ b/actions/robotstxt.php @@ -0,0 +1,100 @@ + + * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 + * @link http://status.net/ + * + * 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 . + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +/** + * Prints out a static robots.txt + * + * @category Action + * @package StatusNet + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 + * @link http://status.net/ + */ + +class RobotstxtAction extends Action +{ + /** + * Handles requests + * + * Since this is a relatively static document, we + * don't do a prepare() + * + * @param array $args GET, POST, and URL params; unused. + * + * @return void + */ + + function handle($args) + { + if (Event::handle('StartRobotsTxt', array($this))) { + + header('Content-Type: text/plain'); + + print "User-Agent: *\n"; + + if (common_config('site', 'private')) { + + print "Disallow: /\n"; + + } else { + + $disallow = common_config('robotstxt', 'disallow'); + + foreach ($disallow as $dir) { + print "Disallow: /$dir/\n"; + } + + $crawldelay = common_config('robotstxt', 'crawldelay'); + + if (!empty($crawldelay)) { + print "Crawl-delay: " . $crawldelay . "\n"; + } + } + + Event::handle('EndRobotsTxt', array($this)); + } + } + + /** + * Return true; this page doesn't touch the DB. + * + * @param array $args other arguments + * + * @return boolean is read only action? + */ + + function isReadOnly($args) + { + return true; + } +} diff --git a/index.php b/index.php index 605b380bf8..06ff9900fd 100644 --- a/index.php +++ b/index.php @@ -285,8 +285,9 @@ function main() if (!$user && common_config('site', 'private') && !isLoginAction($action) && !preg_match('/rss$/', $action) - && !preg_match('/^Api/', $action) - ) { + && $action != 'robotstxt' + && !preg_match('/^Api/', $action)) { + // set returnto $rargs =& common_copy_args($args); unset($rargs['action']); diff --git a/lib/default.php b/lib/default.php index 1337a96336..2bedc4bf08 100644 --- a/lib/default.php +++ b/lib/default.php @@ -270,4 +270,8 @@ $default = 'singleuser' => array('enabled' => false, 'nickname' => null), + 'robotstxt' => + array('crawldelay' => 0, + 'disallow' => array('main', 'settings', 'admin', 'search', 'message') + ), ); diff --git a/lib/router.php b/lib/router.php index ca9f328126..4b5b8d0bb8 100644 --- a/lib/router.php +++ b/lib/router.php @@ -73,6 +73,8 @@ class Router if (Event::handle('StartInitializeRouter', array(&$m))) { + $m->connect('robots.txt', array('action' => 'robotstxt')); + $m->connect('opensearch/people', array('action' => 'opensearch', 'type' => 'people')); $m->connect('opensearch/notice', array('action' => 'opensearch', From 30268cff7899519f249861e067e3af680ee1c570 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sun, 31 Jan 2010 15:16:59 -0500 Subject: [PATCH 121/170] Add Really Simple Discovery (RSD) support Anil Dash suggested that all implementers of the Twitter API include support for the remedial RSD format. This commit adds an RSD action that returns the API root and additional API data to help client developers discover and use our Twitter-compatible API. http://dashes.com/anil/2009/12/the-twitter-api-is-finished.html http://tales.phrasewise.com/rfc/rsd --- actions/public.php | 10 +- actions/rsd.php | 226 +++++++++++++++++++++++++++++++++++++++++ actions/showstream.php | 9 ++ lib/router.php | 9 ++ 4 files changed, 253 insertions(+), 1 deletion(-) create mode 100644 actions/rsd.php diff --git a/actions/public.php b/actions/public.php index 982dfde157..50278bfced 100644 --- a/actions/public.php +++ b/actions/public.php @@ -131,12 +131,20 @@ class PublicAction extends Action return _('Public timeline'); } } - + function extraHead() { parent::extraHead(); $this->element('meta', array('http-equiv' => 'X-XRDS-Location', 'content' => common_local_url('publicxrds'))); + + $rsd = common_local_url('rsd'); + + // RSD, http://tales.phrasewise.com/rfc/rsd + + $this->element('link', array('rel' => 'EditURI', + 'type' => 'application/rsd+xml', + 'href' => $rsd)); } /** diff --git a/actions/rsd.php b/actions/rsd.php new file mode 100644 index 0000000000..f88bf2e9a8 --- /dev/null +++ b/actions/rsd.php @@ -0,0 +1,226 @@ +. + * + * @category API + * @package StatusNet + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 + * @link http://status.net/ + * + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +/** + * RSD action class + * + * Really Simple Discovery (RSD) is a simple (to a fault, maybe) + * discovery tool for blog APIs. + * + * http://tales.phrasewise.com/rfc/rsd + * + * Anil Dash suggested that RSD be used for services that implement + * the Twitter API: + * + * http://dashes.com/anil/2009/12/the-twitter-api-is-finished.html + * + * It's in use now for WordPress.com blogs: + * + * http://matt.wordpress.com/xmlrpc.php?rsd + * + * I (evan@status.net) have tried to stay faithful to the premise of + * RSD, while adding information useful to StatusNet client developers. + * In particular: + * + * - There is a link from each user's profile page to their personal + * RSD feed. A personal rsd.xml includes a 'blogID' element that is + * their username. + * - There is a link from the public root to '/rsd.xml', a public RSD + * feed. It's identical to the personal rsd except it doesn't include + * a blogId. + * - I've added a setting to the API to indicate that OAuth support is + * available. + * + * @category API + * @package StatusNet + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 + * @link http://status.net/ + */ + +class RsdAction extends Action +{ + /** + * Optional attribute for the personal rsd.xml file. + */ + + var $user = null; + + /** + * Prepare the action for use. + * + * Check for a nickname; redirect if non-canonical; if + * not provided, assume public rsd.xml. + * + * @param array $args GET, POST, and URI arguments. + * + * @return boolean success flag + */ + + function prepare($args) + { + parent::prepare($args); + + // optional argument + + $nickname_arg = $this->arg('nickname'); + + if (empty($nickname_arg)) { + $this->user = null; + } else { + $nickname = common_canonical_nickname($nickname_arg); + + // Permanent redirect on non-canonical nickname + + if ($nickname_arg != $nickname) { + common_redirect(common_local_url('rsd', + array('nickname' => $nickname)), + 301); + return false; + } + + $this->user = User::staticGet('nickname', $nickname); + + if (empty($this->user)) { + $this->clientError(_('No such user.'), 404); + return false; + } + } + + return true; + } + + /** + * Action handler. + * + * Outputs the XML format for an RSD file. May include + * personal information if this is a personal file + * (based on whether $user attribute is set). + * + * @param array $args array of arguments + * + * @return nothing + */ + + function handle($args) + { + header('Content-Type: application/rsd+xml'); + + $this->startXML(); + + $rsdNS = 'http://archipelago.phrasewise.com/rsd'; + $this->elementStart('rsd', array('version' => '1.0', + 'xmlns' => $rsdNS)); + $this->elementStart('service'); + $this->element('engineName', null, _('StatusNet')); + $this->element('engineLink', null, 'http://status.net/'); + $this->elementStart('apis'); + if (Event::handle('StartRsdListApis', array($this, $this->user))) { + + $blogID = (empty($this->user)) ? '' : $this->user->nickname; + $apiAttrs = array('name' => 'Twitter', + 'preferred' => 'true', + 'apiLink' => $this->_apiRoot(), + 'blogID' => $blogID); + + $this->elementStart('api', $apiAttrs); + $this->elementStart('settings'); + $this->element('docs', null, + 'http://status.net/wiki/TwitterCompatibleAPI'); + $this->element('setting', array('name' => 'OAuth'), + 'true'); + $this->elementEnd('settings'); + $this->elementEnd('api'); + Event::handle('EndRsdListApis', array($this, $this->user)); + } + $this->elementEnd('apis'); + $this->elementEnd('service'); + $this->elementEnd('rsd'); + + $this->endXML(); + + return true; + } + + /** + * Returns last-modified date for use in caching + * + * Per-user rsd.xml is dated to last change of user + * (in case of nickname change); public has no date. + * + * @return string date of last change of this page + */ + + function lastModified() + { + if (!empty($this->user)) { + return $this->user->modified; + } else { + return null; + } + } + + /** + * Flag to indicate if this action is read-only + * + * It is; it doesn't change the DB. + * + * @param array $args ignored + * + * @return boolean true + */ + + function isReadOnly($args) + { + return true; + } + + /** + * Return current site's API root + * + * Varies based on URL parameters, like if fancy URLs are + * turned on. + * + * @return string API root URI for this site + */ + + private function _apiRoot() + { + if (common_config('site', 'fancy')) { + return common_path('api/', true); + } else { + return common_path('index.php/api/', true); + } + } +} diff --git a/actions/showstream.php b/actions/showstream.php index c529193860..07cc68b765 100644 --- a/actions/showstream.php +++ b/actions/showstream.php @@ -178,6 +178,15 @@ class ShowstreamAction extends ProfileAction $this->element('link', array('rel' => 'microsummary', 'href' => common_local_url('microsummary', array('nickname' => $this->profile->nickname)))); + + $rsd = common_local_url('rsd', + array('nickname' => $this->profile->nickname)); + + // RSD, http://tales.phrasewise.com/rfc/rsd + $this->element('link', array('rel' => 'EditURI', + 'type' => 'application/rsd+xml', + 'href' => $rsd)); + } function showProfile() diff --git a/lib/router.php b/lib/router.php index 4b5b8d0bb8..b046b240c9 100644 --- a/lib/router.php +++ b/lib/router.php @@ -708,6 +708,10 @@ class Router 'nickname' => $nickname), array('tag' => '[a-zA-Z0-9]+')); + $m->connect('rsd.xml', + array('action' => 'rsd', + 'nickname' => $nickname)); + $m->connect('', array('action' => 'showstream', 'nickname' => $nickname)); @@ -722,6 +726,7 @@ class Router $m->connect('featured', array('action' => 'featured')); $m->connect('favorited/', array('action' => 'favorited')); $m->connect('favorited', array('action' => 'favorited')); + $m->connect('rsd.xml', array('action' => 'rsd')); foreach (array('subscriptions', 'subscribers', 'nudge', 'all', 'foaf', 'xrds', @@ -769,6 +774,10 @@ class Router array('nickname' => '[a-zA-Z0-9]{1,64}'), array('tag' => '[a-zA-Z0-9]+')); + $m->connect(':nickname/rsd.xml', + array('action' => 'rsd'), + array('nickname' => '[a-zA-Z0-9]{1,64}')); + $m->connect(':nickname', array('action' => 'showstream'), array('nickname' => '[a-zA-Z0-9]{1,64}')); From 81087e45c5b797028e90181459e4c673cd7be278 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sun, 31 Jan 2010 15:25:59 -0500 Subject: [PATCH 122/170] move schema.type.php to typeschema.php like other files --- lib/{schema.mysql.php => mysqlschema.php} | 0 lib/{schema.pgsql.php => pgsqlschema.php} | 0 lib/schema.php | 8 +++----- 3 files changed, 3 insertions(+), 5 deletions(-) rename lib/{schema.mysql.php => mysqlschema.php} (100%) rename lib/{schema.pgsql.php => pgsqlschema.php} (100%) diff --git a/lib/schema.mysql.php b/lib/mysqlschema.php similarity index 100% rename from lib/schema.mysql.php rename to lib/mysqlschema.php diff --git a/lib/schema.pgsql.php b/lib/pgsqlschema.php similarity index 100% rename from lib/schema.pgsql.php rename to lib/pgsqlschema.php diff --git a/lib/schema.php b/lib/schema.php index 27a4deda11..137b814e02 100644 --- a/lib/schema.php +++ b/lib/schema.php @@ -77,14 +77,12 @@ class Schema { $type = common_config('db', 'type'); if (empty(self::$_single)) { - include "lib/schema.{$type}.php"; - $class = $type.='Schema'; - self::$_single = new $class(); + $schemaClass = ucfirst($type).'Schema'; + self::$_single = new $schemaClass(); } return self::$_single; } - /** * Gets a ColumnDef object for a single column. * @@ -475,7 +473,7 @@ class Schema } else { $sql .= ($cd->nullable) ? "null " : "not null "; } - + if (!empty($cd->auto_increment)) { $sql .= " auto_increment "; } From 99a7dd64f65c0fbe9e1e18e1706527aa0521387c Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Sun, 31 Jan 2010 22:33:11 +0000 Subject: [PATCH 123/170] Updated jQuery JavaScript Library from v1.3.2 to v1.4.1 --- js/jquery.js | 6711 +++++++++++++++++++++++++++++----------------- js/jquery.min.js | 164 +- 2 files changed, 4356 insertions(+), 2519 deletions(-) diff --git a/js/jquery.js b/js/jquery.js index 926357433e..237e1b9081 100644 --- a/js/jquery.js +++ b/js/jquery.js @@ -1,136 +1,221 @@ /*! - * jQuery JavaScript Library v1.3.2 + * jQuery JavaScript Library v1.4.1 * http://jquery.com/ * - * Copyright (c) 2009 John Resig - * Dual licensed under the MIT and GPL licenses. - * http://docs.jquery.com/License + * Copyright 2010, John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license * - * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009) - * Revision: 6246 + * Includes Sizzle.js + * http://sizzlejs.com/ + * Copyright 2010, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * + * Date: Mon Jan 25 19:43:33 2010 -0500 */ -(function(){ +(function( window, undefined ) { -var - // Will speed up references to window, and allows munging its name. - window = this, - // Will speed up references to undefined, and allows munging its name. - undefined, - // Map over jQuery in case of overwrite - _jQuery = window.jQuery, - // Map over the $ in case of overwrite - _$ = window.$, - - jQuery = window.jQuery = window.$ = function( selector, context ) { +// Define a local copy of jQuery +var jQuery = function( selector, context ) { // The jQuery object is actually just the init constructor 'enhanced' return new jQuery.fn.init( selector, context ); }, + // Map over jQuery in case of overwrite + _jQuery = window.jQuery, + + // Map over the $ in case of overwrite + _$ = window.$, + + // Use the correct document accordingly with window argument (sandbox) + document = window.document, + + // A central reference to the root jQuery(document) + rootjQuery, + // A simple way to check for HTML strings or ID strings // (both of which we optimize for) - quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/, + quickExpr = /^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/, + // Is it a simple selector - isSimple = /^.[^:#\[\.,]*$/; + isSimple = /^.[^:#\[\.,]*$/, + + // Check if a string has a non-whitespace character in it + rnotwhite = /\S/, + + // Used for trimming whitespace + rtrim = /^(\s|\u00A0)+|(\s|\u00A0)+$/g, + + // Match a standalone tag + rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/, + + // Keep a UserAgent string for use with jQuery.browser + userAgent = navigator.userAgent, + + // For matching the engine and version of the browser + browserMatch, + + // Has the ready events already been bound? + readyBound = false, + + // The functions to execute on DOM ready + readyList = [], + + // The ready event handler + DOMContentLoaded, + + // Save a reference to some core methods + toString = Object.prototype.toString, + hasOwnProperty = Object.prototype.hasOwnProperty, + push = Array.prototype.push, + slice = Array.prototype.slice, + indexOf = Array.prototype.indexOf; jQuery.fn = jQuery.prototype = { init: function( selector, context ) { - // Make sure that a selection was provided - selector = selector || document; + var match, elem, ret, doc; + + // Handle $(""), $(null), or $(undefined) + if ( !selector ) { + return this; + } // Handle $(DOMElement) if ( selector.nodeType ) { - this[0] = selector; + this.context = this[0] = selector; this.length = 1; - this.context = selector; return this; } + // Handle HTML strings if ( typeof selector === "string" ) { // Are we dealing with HTML string or an ID? - var match = quickExpr.exec( selector ); + match = quickExpr.exec( selector ); // Verify a match, and that no context was specified for #id if ( match && (match[1] || !context) ) { // HANDLE: $(html) -> $(array) - if ( match[1] ) - selector = jQuery.clean( [ match[1] ], context ); + if ( match[1] ) { + doc = (context ? context.ownerDocument || context : document); + + // If a single string is passed in and it's a single tag + // just do a createElement and skip the rest + ret = rsingleTag.exec( selector ); + + if ( ret ) { + if ( jQuery.isPlainObject( context ) ) { + selector = [ document.createElement( ret[1] ) ]; + jQuery.fn.attr.call( selector, context, true ); + + } else { + selector = [ doc.createElement( ret[1] ) ]; + } + + } else { + ret = buildFragment( [ match[1] ], [ doc ] ); + selector = (ret.cacheable ? ret.fragment.cloneNode(true) : ret.fragment).childNodes; + } // HANDLE: $("#id") - else { - var elem = document.getElementById( match[3] ); + } else { + elem = document.getElementById( match[2] ); - // Handle the case where IE and Opera return items - // by name instead of ID - if ( elem && elem.id != match[3] ) - return jQuery().find( selector ); + if ( elem ) { + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id !== match[2] ) { + return rootjQuery.find( selector ); + } - // Otherwise, we inject the element directly into the jQuery object - var ret = jQuery( elem || [] ); - ret.context = document; - ret.selector = selector; - return ret; + // Otherwise, we inject the element directly into the jQuery object + this.length = 1; + this[0] = elem; + } + + this.context = document; + this.selector = selector; + return this; } - // HANDLE: $(expr, [context]) - // (which is just equivalent to: $(content).find(expr) - } else + // HANDLE: $("TAG") + } else if ( !context && /^\w+$/.test( selector ) ) { + this.selector = selector; + this.context = document; + selector = document.getElementsByTagName( selector ); + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return (context || rootjQuery).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { return jQuery( context ).find( selector ); + } // HANDLE: $(function) // Shortcut for document ready - } else if ( jQuery.isFunction( selector ) ) - return jQuery( document ).ready( selector ); + } else if ( jQuery.isFunction( selector ) ) { + return rootjQuery.ready( selector ); + } - // Make sure that old selector state is passed along - if ( selector.selector && selector.context ) { + if (selector.selector !== undefined) { this.selector = selector.selector; this.context = selector.context; } - return this.setArray(jQuery.isArray( selector ) ? - selector : - jQuery.makeArray(selector)); + return jQuery.isArray( selector ) ? + this.setArray( selector ) : + jQuery.makeArray( selector, this ); }, // Start with an empty selector selector: "", // The current version of jQuery being used - jquery: "1.3.2", + jquery: "1.4.1", + + // The default length of a jQuery object is 0 + length: 0, // The number of elements contained in the matched element set size: function() { return this.length; }, + toArray: function() { + return slice.call( this, 0 ); + }, + // Get the Nth element in the matched element set OR // Get the whole matched element set as a clean array get: function( num ) { - return num === undefined ? + return num == null ? // Return a 'clean' array - Array.prototype.slice.call( this ) : + this.toArray() : // Return just the object - this[ num ]; + ( num < 0 ? this.slice(num)[ 0 ] : this[ num ] ); }, // Take an array of elements and push it onto the stack // (returning the new matched element set) pushStack: function( elems, name, selector ) { // Build a new jQuery matched element set - var ret = jQuery( elems ); + var ret = jQuery( elems || null ); // Add the old object onto the stack (as a reference) ret.prevObject = this; ret.context = this.context; - if ( name === "find" ) + if ( name === "find" ) { ret.selector = this.selector + (this.selector ? " " : "") + selector; - else if ( name ) + } else if ( name ) { ret.selector = this.selector + "." + name + "(" + selector + ")"; + } // Return the newly-formed element set return ret; @@ -143,7 +228,7 @@ jQuery.fn = jQuery.prototype = { // Resetting the length to 0, then using the native Array push // is a super-fast way to populate an object with array-like properties this.length = 0; - Array.prototype.push.apply( this, elems ); + push.apply( this, elems ); return this; }, @@ -154,414 +239,67 @@ jQuery.fn = jQuery.prototype = { each: function( callback, args ) { return jQuery.each( this, callback, args ); }, + + ready: function( fn ) { + // Attach the listeners + jQuery.bindReady(); - // Determine the position of an element within - // the matched set of elements - index: function( elem ) { - // Locate the position of the desired element - return jQuery.inArray( - // If it receives a jQuery object, the first element is used - elem && elem.jquery ? elem[0] : elem - , this ); - }, + // If the DOM is already ready + if ( jQuery.isReady ) { + // Execute the function immediately + fn.call( document, jQuery ); - attr: function( name, value, type ) { - var options = name; - - // Look for the case where we're accessing a style value - if ( typeof name === "string" ) - if ( value === undefined ) - return this[0] && jQuery[ type || "attr" ]( this[0], name ); - - else { - options = {}; - options[ name ] = value; - } - - // Check to see if we're setting style values - return this.each(function(i){ - // Set all the styles - for ( name in options ) - jQuery.attr( - type ? - this.style : - this, - name, jQuery.prop( this, options[ name ], type, i, name ) - ); - }); - }, - - css: function( key, value ) { - // ignore negative width and height values - if ( (key == 'width' || key == 'height') && parseFloat(value) < 0 ) - value = undefined; - return this.attr( key, value, "curCSS" ); - }, - - text: function( text ) { - if ( typeof text !== "object" && text != null ) - return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) ); - - var ret = ""; - - jQuery.each( text || this, function(){ - jQuery.each( this.childNodes, function(){ - if ( this.nodeType != 8 ) - ret += this.nodeType != 1 ? - this.nodeValue : - jQuery.fn.text( [ this ] ); - }); - }); - - return ret; - }, - - wrapAll: function( html ) { - if ( this[0] ) { - // The elements to wrap the target around - var wrap = jQuery( html, this[0].ownerDocument ).clone(); - - if ( this[0].parentNode ) - wrap.insertBefore( this[0] ); - - wrap.map(function(){ - var elem = this; - - while ( elem.firstChild ) - elem = elem.firstChild; - - return elem; - }).append(this); + // Otherwise, remember the function for later + } else if ( readyList ) { + // Add the function to the wait list + readyList.push( fn ); } return this; }, - - wrapInner: function( html ) { - return this.each(function(){ - jQuery( this ).contents().wrapAll( html ); - }); + + eq: function( i ) { + return i === -1 ? + this.slice( i ) : + this.slice( i, +i + 1 ); }, - wrap: function( html ) { - return this.each(function(){ - jQuery( this ).wrapAll( html ); - }); + first: function() { + return this.eq( 0 ); }, - append: function() { - return this.domManip(arguments, true, function(elem){ - if (this.nodeType == 1) - this.appendChild( elem ); - }); + last: function() { + return this.eq( -1 ); }, - prepend: function() { - return this.domManip(arguments, true, function(elem){ - if (this.nodeType == 1) - this.insertBefore( elem, this.firstChild ); - }); + slice: function() { + return this.pushStack( slice.apply( this, arguments ), + "slice", slice.call(arguments).join(",") ); }, - before: function() { - return this.domManip(arguments, false, function(elem){ - this.parentNode.insertBefore( elem, this ); - }); + map: function( callback ) { + return this.pushStack( jQuery.map(this, function( elem, i ) { + return callback.call( elem, i, elem ); + })); }, - - after: function() { - return this.domManip(arguments, false, function(elem){ - this.parentNode.insertBefore( elem, this.nextSibling ); - }); - }, - + end: function() { - return this.prevObject || jQuery( [] ); + return this.prevObject || jQuery(null); }, // For internal use only. // Behaves like an Array's method, not like a jQuery method. - push: [].push, + push: push, sort: [].sort, - splice: [].splice, - - find: function( selector ) { - if ( this.length === 1 ) { - var ret = this.pushStack( [], "find", selector ); - ret.length = 0; - jQuery.find( selector, this[0], ret ); - return ret; - } else { - return this.pushStack( jQuery.unique(jQuery.map(this, function(elem){ - return jQuery.find( selector, elem ); - })), "find", selector ); - } - }, - - clone: function( events ) { - // Do the clone - var ret = this.map(function(){ - if ( !jQuery.support.noCloneEvent && !jQuery.isXMLDoc(this) ) { - // IE copies events bound via attachEvent when - // using cloneNode. Calling detachEvent on the - // clone will also remove the events from the orignal - // In order to get around this, we use innerHTML. - // Unfortunately, this means some modifications to - // attributes in IE that are actually only stored - // as properties will not be copied (such as the - // the name attribute on an input). - var html = this.outerHTML; - if ( !html ) { - var div = this.ownerDocument.createElement("div"); - div.appendChild( this.cloneNode(true) ); - html = div.innerHTML; - } - - return jQuery.clean([html.replace(/ jQuery\d+="(?:\d+|null)"/g, "").replace(/^\s*/, "")])[0]; - } else - return this.cloneNode(true); - }); - - // Copy the events from the original to the clone - if ( events === true ) { - var orig = this.find("*").andSelf(), i = 0; - - ret.find("*").andSelf().each(function(){ - if ( this.nodeName !== orig[i].nodeName ) - return; - - var events = jQuery.data( orig[i], "events" ); - - for ( var type in events ) { - for ( var handler in events[ type ] ) { - jQuery.event.add( this, type, events[ type ][ handler ], events[ type ][ handler ].data ); - } - } - - i++; - }); - } - - // Return the cloned set - return ret; - }, - - filter: function( selector ) { - return this.pushStack( - jQuery.isFunction( selector ) && - jQuery.grep(this, function(elem, i){ - return selector.call( elem, i ); - }) || - - jQuery.multiFilter( selector, jQuery.grep(this, function(elem){ - return elem.nodeType === 1; - }) ), "filter", selector ); - }, - - closest: function( selector ) { - var pos = jQuery.expr.match.POS.test( selector ) ? jQuery(selector) : null, - closer = 0; - - return this.map(function(){ - var cur = this; - while ( cur && cur.ownerDocument ) { - if ( pos ? pos.index(cur) > -1 : jQuery(cur).is(selector) ) { - jQuery.data(cur, "closest", closer); - return cur; - } - cur = cur.parentNode; - closer++; - } - }); - }, - - not: function( selector ) { - if ( typeof selector === "string" ) - // test special case where just one selector is passed in - if ( isSimple.test( selector ) ) - return this.pushStack( jQuery.multiFilter( selector, this, true ), "not", selector ); - else - selector = jQuery.multiFilter( selector, this ); - - var isArrayLike = selector.length && selector[selector.length - 1] !== undefined && !selector.nodeType; - return this.filter(function() { - return isArrayLike ? jQuery.inArray( this, selector ) < 0 : this != selector; - }); - }, - - add: function( selector ) { - return this.pushStack( jQuery.unique( jQuery.merge( - this.get(), - typeof selector === "string" ? - jQuery( selector ) : - jQuery.makeArray( selector ) - ))); - }, - - is: function( selector ) { - return !!selector && jQuery.multiFilter( selector, this ).length > 0; - }, - - hasClass: function( selector ) { - return !!selector && this.is( "." + selector ); - }, - - val: function( value ) { - if ( value === undefined ) { - var elem = this[0]; - - if ( elem ) { - if( jQuery.nodeName( elem, 'option' ) ) - return (elem.attributes.value || {}).specified ? elem.value : elem.text; - - // We need to handle select boxes special - if ( jQuery.nodeName( elem, "select" ) ) { - var index = elem.selectedIndex, - values = [], - options = elem.options, - one = elem.type == "select-one"; - - // Nothing was selected - if ( index < 0 ) - return null; - - // Loop through all the selected options - for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) { - var option = options[ i ]; - - if ( option.selected ) { - // Get the specifc value for the option - value = jQuery(option).val(); - - // We don't need an array for one selects - if ( one ) - return value; - - // Multi-Selects return an array - values.push( value ); - } - } - - return values; - } - - // Everything else, we just grab the value - return (elem.value || "").replace(/\r/g, ""); - - } - - return undefined; - } - - if ( typeof value === "number" ) - value += ''; - - return this.each(function(){ - if ( this.nodeType != 1 ) - return; - - if ( jQuery.isArray(value) && /radio|checkbox/.test( this.type ) ) - this.checked = (jQuery.inArray(this.value, value) >= 0 || - jQuery.inArray(this.name, value) >= 0); - - else if ( jQuery.nodeName( this, "select" ) ) { - var values = jQuery.makeArray(value); - - jQuery( "option", this ).each(function(){ - this.selected = (jQuery.inArray( this.value, values ) >= 0 || - jQuery.inArray( this.text, values ) >= 0); - }); - - if ( !values.length ) - this.selectedIndex = -1; - - } else - this.value = value; - }); - }, - - html: function( value ) { - return value === undefined ? - (this[0] ? - this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g, "") : - null) : - this.empty().append( value ); - }, - - replaceWith: function( value ) { - return this.after( value ).remove(); - }, - - eq: function( i ) { - return this.slice( i, +i + 1 ); - }, - - slice: function() { - return this.pushStack( Array.prototype.slice.apply( this, arguments ), - "slice", Array.prototype.slice.call(arguments).join(",") ); - }, - - map: function( callback ) { - return this.pushStack( jQuery.map(this, function(elem, i){ - return callback.call( elem, i, elem ); - })); - }, - - andSelf: function() { - return this.add( this.prevObject ); - }, - - domManip: function( args, table, callback ) { - if ( this[0] ) { - var fragment = (this[0].ownerDocument || this[0]).createDocumentFragment(), - scripts = jQuery.clean( args, (this[0].ownerDocument || this[0]), fragment ), - first = fragment.firstChild; - - if ( first ) - for ( var i = 0, l = this.length; i < l; i++ ) - callback.call( root(this[i], first), this.length > 1 || i > 0 ? - fragment.cloneNode(true) : fragment ); - - if ( scripts ) - jQuery.each( scripts, evalScript ); - } - - return this; - - function root( elem, cur ) { - return table && jQuery.nodeName(elem, "table") && jQuery.nodeName(cur, "tr") ? - (elem.getElementsByTagName("tbody")[0] || - elem.appendChild(elem.ownerDocument.createElement("tbody"))) : - elem; - } - } + splice: [].splice }; // Give the init function the jQuery prototype for later instantiation jQuery.fn.init.prototype = jQuery.fn; -function evalScript( i, elem ) { - if ( elem.src ) - jQuery.ajax({ - url: elem.src, - async: false, - dataType: "script" - }); - - else - jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" ); - - if ( elem.parentNode ) - elem.parentNode.removeChild( elem ); -} - -function now(){ - return +new Date; -} - jQuery.extend = jQuery.fn.extend = function() { // copy reference to target object - var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options; + var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options, name, src, copy; // Handle a deep copy situation if ( typeof target === "boolean" ) { @@ -572,58 +310,137 @@ jQuery.extend = jQuery.fn.extend = function() { } // Handle case when target is a string or something (possible in deep copy) - if ( typeof target !== "object" && !jQuery.isFunction(target) ) + if ( typeof target !== "object" && !jQuery.isFunction(target) ) { target = {}; + } // extend jQuery itself if only one argument is passed - if ( length == i ) { + if ( length === i ) { target = this; --i; } - for ( ; i < length; i++ ) + for ( ; i < length; i++ ) { // Only deal with non-null/undefined values - if ( (options = arguments[ i ]) != null ) + if ( (options = arguments[ i ]) != null ) { // Extend the base object - for ( var name in options ) { - var src = target[ name ], copy = options[ name ]; + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; // Prevent never-ending loop - if ( target === copy ) + if ( target === copy ) { continue; + } - // Recurse if we're merging object values - if ( deep && copy && typeof copy === "object" && !copy.nodeType ) - target[ name ] = jQuery.extend( deep, - // Never move original objects, clone them - src || ( copy.length != null ? [ ] : { } ) - , copy ); + // Recurse if we're merging object literal values or arrays + if ( deep && copy && ( jQuery.isPlainObject(copy) || jQuery.isArray(copy) ) ) { + var clone = src && ( jQuery.isPlainObject(src) || jQuery.isArray(src) ) ? src + : jQuery.isArray(copy) ? [] : {}; + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); // Don't bring in undefined values - else if ( copy !== undefined ) + } else if ( copy !== undefined ) { target[ name ] = copy; - + } } + } + } // Return the modified object return target; }; -// exclude the following css properties to add px -var exclude = /z-?index|font-?weight|opacity|zoom|line-?height/i, - // cache defaultView - defaultView = document.defaultView || {}, - toString = Object.prototype.toString; - jQuery.extend({ noConflict: function( deep ) { window.$ = _$; - if ( deep ) + if ( deep ) { window.jQuery = _jQuery; + } return jQuery; }, + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // Handle when the DOM is ready + ready: function() { + // Make sure that the DOM is not already loaded + if ( !jQuery.isReady ) { + // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). + if ( !document.body ) { + return setTimeout( jQuery.ready, 13 ); + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If there are functions bound, to execute + if ( readyList ) { + // Execute all of them + var fn, i = 0; + while ( (fn = readyList[ i++ ]) ) { + fn.call( document, jQuery ); + } + + // Reset the list of functions + readyList = null; + } + + // Trigger any bound ready events + if ( jQuery.fn.triggerHandler ) { + jQuery( document ).triggerHandler( "ready" ); + } + } + }, + + bindReady: function() { + if ( readyBound ) { + return; + } + + readyBound = true; + + // Catch cases where $(document).ready() is called after the + // browser event has already occurred. + if ( document.readyState === "complete" ) { + return jQuery.ready(); + } + + // Mozilla, Opera and webkit nightlies currently support this event + if ( document.addEventListener ) { + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", jQuery.ready, false ); + + // If IE event model is used + } else if ( document.attachEvent ) { + // ensure firing before onload, + // maybe late but safe also for iframes + document.attachEvent("onreadystatechange", DOMContentLoaded); + + // A fallback to window.onload, that will always work + window.attachEvent( "onload", jQuery.ready ); + + // If IE and not a frame + // continually check to see if the document is ready + var toplevel = false; + + try { + toplevel = window.frameElement == null; + } catch(e) {} + + if ( document.documentElement.doScroll && toplevel ) { + doScrollCheck(); + } + } + }, // See test/unit/core.js for details concerning isFunction. // Since version 1.3, DOM methods and functions like alert @@ -636,27 +453,81 @@ jQuery.extend({ return toString.call(obj) === "[object Array]"; }, - // check if an element is in a (or is an) XML document - isXMLDoc: function( elem ) { - return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" || - !!elem.ownerDocument && jQuery.isXMLDoc( elem.ownerDocument ); + isPlainObject: function( obj ) { + // Must be an Object. + // Because of IE, we also have to check the presence of the constructor property. + // Make sure that DOM nodes and window objects don't pass through, as well + if ( !obj || toString.call(obj) !== "[object Object]" || obj.nodeType || obj.setInterval ) { + return false; + } + + // Not own constructor property must be Object + if ( obj.constructor + && !hasOwnProperty.call(obj, "constructor") + && !hasOwnProperty.call(obj.constructor.prototype, "isPrototypeOf") ) { + return false; + } + + // Own properties are enumerated firstly, so to speed up, + // if last one is own, then all properties are own. + + var key; + for ( key in obj ) {} + + return key === undefined || hasOwnProperty.call( obj, key ); }, + isEmptyObject: function( obj ) { + for ( var name in obj ) { + return false; + } + return true; + }, + + error: function( msg ) { + throw msg; + }, + + parseJSON: function( data ) { + if ( typeof data !== "string" || !data ) { + return null; + } + + // Make sure the incoming data is actual JSON + // Logic borrowed from http://json.org/json2.js + if ( /^[\],:{}\s]*$/.test(data.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, "@") + .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, "]") + .replace(/(?:^|:|,)(?:\s*\[)+/g, "")) ) { + + // Try to use the native JSON parser first + return window.JSON && window.JSON.parse ? + window.JSON.parse( data ) : + (new Function("return " + data))(); + + } else { + jQuery.error( "Invalid JSON: " + data ); + } + }, + + noop: function() {}, + // Evalulates a script in a global context globalEval: function( data ) { - if ( data && /\S/.test(data) ) { + if ( data && rnotwhite.test(data) ) { // Inspired by code by Andrea Giammarchi // http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html var head = document.getElementsByTagName("head")[0] || document.documentElement, script = document.createElement("script"); script.type = "text/javascript"; - if ( jQuery.support.scriptEval ) - script.appendChild( document.createTextNode( data ) ); - else - script.text = data; - // Use insertBefore instead of appendChild to circumvent an IE6 bug. + if ( jQuery.support.scriptEval ) { + script.appendChild( document.createTextNode( data ) ); + } else { + script.text = data; + } + + // Use insertBefore instead of appendChild to circumvent an IE6 bug. // This arises when a base node is used (#2709). head.insertBefore( script, head.firstChild ); head.removeChild( script ); @@ -664,471 +535,99 @@ jQuery.extend({ }, nodeName: function( elem, name ) { - return elem.nodeName && elem.nodeName.toUpperCase() == name.toUpperCase(); + return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase(); }, // args is for internal usage only each: function( object, callback, args ) { - var name, i = 0, length = object.length; + var name, i = 0, + length = object.length, + isObj = length === undefined || jQuery.isFunction(object); if ( args ) { - if ( length === undefined ) { - for ( name in object ) - if ( callback.apply( object[ name ], args ) === false ) + if ( isObj ) { + for ( name in object ) { + if ( callback.apply( object[ name ], args ) === false ) { break; - } else - for ( ; i < length; ) - if ( callback.apply( object[ i++ ], args ) === false ) + } + } + } else { + for ( ; i < length; ) { + if ( callback.apply( object[ i++ ], args ) === false ) { break; + } + } + } // A special, fast, case for the most common use of each } else { - if ( length === undefined ) { - for ( name in object ) - if ( callback.call( object[ name ], name, object[ name ] ) === false ) + if ( isObj ) { + for ( name in object ) { + if ( callback.call( object[ name ], name, object[ name ] ) === false ) { break; - } else + } + } + } else { for ( var value = object[0]; - i < length && callback.call( value, i, value ) !== false; value = object[++i] ){} + i < length && callback.call( value, i, value ) !== false; value = object[++i] ) {} + } } return object; }, - prop: function( elem, value, type, i, name ) { - // Handle executable functions - if ( jQuery.isFunction( value ) ) - value = value.call( elem, i ); - - // Handle passing in a number to a CSS property - return typeof value === "number" && type == "curCSS" && !exclude.test( name ) ? - value + "px" : - value; - }, - - className: { - // internal only, use addClass("class") - add: function( elem, classNames ) { - jQuery.each((classNames || "").split(/\s+/), function(i, className){ - if ( elem.nodeType == 1 && !jQuery.className.has( elem.className, className ) ) - elem.className += (elem.className ? " " : "") + className; - }); - }, - - // internal only, use removeClass("class") - remove: function( elem, classNames ) { - if (elem.nodeType == 1) - elem.className = classNames !== undefined ? - jQuery.grep(elem.className.split(/\s+/), function(className){ - return !jQuery.className.has( classNames, className ); - }).join(" ") : - ""; - }, - - // internal only, use hasClass("class") - has: function( elem, className ) { - return elem && jQuery.inArray( className, (elem.className || elem).toString().split(/\s+/) ) > -1; - } - }, - - // A method for quickly swapping in/out CSS properties to get correct calculations - swap: function( elem, options, callback ) { - var old = {}; - // Remember the old values, and insert the new ones - for ( var name in options ) { - old[ name ] = elem.style[ name ]; - elem.style[ name ] = options[ name ]; - } - - callback.call( elem ); - - // Revert the old values - for ( var name in options ) - elem.style[ name ] = old[ name ]; - }, - - css: function( elem, name, force, extra ) { - if ( name == "width" || name == "height" ) { - var val, props = { position: "absolute", visibility: "hidden", display:"block" }, which = name == "width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ]; - - function getWH() { - val = name == "width" ? elem.offsetWidth : elem.offsetHeight; - - if ( extra === "border" ) - return; - - jQuery.each( which, function() { - if ( !extra ) - val -= parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0; - if ( extra === "margin" ) - val += parseFloat(jQuery.curCSS( elem, "margin" + this, true)) || 0; - else - val -= parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0; - }); - } - - if ( elem.offsetWidth !== 0 ) - getWH(); - else - jQuery.swap( elem, props, getWH ); - - return Math.max(0, Math.round(val)); - } - - return jQuery.curCSS( elem, name, force ); - }, - - curCSS: function( elem, name, force ) { - var ret, style = elem.style; - - // We need to handle opacity special in IE - if ( name == "opacity" && !jQuery.support.opacity ) { - ret = jQuery.attr( style, "opacity" ); - - return ret == "" ? - "1" : - ret; - } - - // Make sure we're using the right name for getting the float value - if ( name.match( /float/i ) ) - name = styleFloat; - - if ( !force && style && style[ name ] ) - ret = style[ name ]; - - else if ( defaultView.getComputedStyle ) { - - // Only "float" is needed here - if ( name.match( /float/i ) ) - name = "float"; - - name = name.replace( /([A-Z])/g, "-$1" ).toLowerCase(); - - var computedStyle = defaultView.getComputedStyle( elem, null ); - - if ( computedStyle ) - ret = computedStyle.getPropertyValue( name ); - - // We should always get a number back from opacity - if ( name == "opacity" && ret == "" ) - ret = "1"; - - } else if ( elem.currentStyle ) { - var camelCase = name.replace(/\-(\w)/g, function(all, letter){ - return letter.toUpperCase(); - }); - - ret = elem.currentStyle[ name ] || elem.currentStyle[ camelCase ]; - - // From the awesome hack by Dean Edwards - // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 - - // If we're not dealing with a regular pixel number - // but a number that has a weird ending, we need to convert it to pixels - if ( !/^\d+(px)?$/i.test( ret ) && /^\d/.test( ret ) ) { - // Remember the original values - var left = style.left, rsLeft = elem.runtimeStyle.left; - - // Put in the new values to get a computed value out - elem.runtimeStyle.left = elem.currentStyle.left; - style.left = ret || 0; - ret = style.pixelLeft + "px"; - - // Revert the changed values - style.left = left; - elem.runtimeStyle.left = rsLeft; - } - } - - return ret; - }, - - clean: function( elems, context, fragment ) { - context = context || document; - - // !context.createElement fails in IE with an error but returns typeof 'object' - if ( typeof context.createElement === "undefined" ) - context = context.ownerDocument || context[0] && context[0].ownerDocument || document; - - // If a single string is passed in and it's a single tag - // just do a createElement and skip the rest - if ( !fragment && elems.length === 1 && typeof elems[0] === "string" ) { - var match = /^<(\w+)\s*\/?>$/.exec(elems[0]); - if ( match ) - return [ context.createElement( match[1] ) ]; - } - - var ret = [], scripts = [], div = context.createElement("div"); - - jQuery.each(elems, function(i, elem){ - if ( typeof elem === "number" ) - elem += ''; - - if ( !elem ) - return; - - // Convert html string into DOM nodes - if ( typeof elem === "string" ) { - // Fix "XHTML"-style tags in all browsers - elem = elem.replace(/(<(\w+)[^>]*?)\/>/g, function(all, front, tag){ - return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i) ? - all : - front + ">"; - }); - - // Trim whitespace, otherwise indexOf won't work as expected - var tags = elem.replace(/^\s+/, "").substring(0, 10).toLowerCase(); - - var wrap = - // option or optgroup - !tags.indexOf("", "" ] || - - !tags.indexOf("", "" ] || - - tags.match(/^<(thead|tbody|tfoot|colg|cap)/) && - [ 1, "", "
    " ] || - - !tags.indexOf("", "" ] || - - // matched above - (!tags.indexOf("", "" ] || - - !tags.indexOf("", "" ] || - - // IE can't serialize and