From cae2429d12321319d4a9ef1a22d73aaf76449a36 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Sat, 16 Jan 2010 19:44:37 +0000 Subject: 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 88016bd6d..ef28f31e8 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); -- cgit v1.2.3-54-g00ecf From f9cc3e6d942f4a1e3b9e000daa5ecca4df5e4ce5 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Sat, 16 Jan 2010 20:10:46 +0000 Subject: 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 ef28f31e8..373a4f3b0 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(); -- cgit v1.2.3-54-g00ecf From 2e643071ff3d03bd05f453b8e144aee4695e952e Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Sun, 17 Jan 2010 22:31:47 +0000 Subject: 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 373a4f3b0..a10e9d15a 100644 --- a/js/util.js +++ b/js/util.js @@ -624,8 +624,6 @@ var SN = { // StatusNet else { removeNoticeDataGeo(); } - - $('#'+SN.C.S.NoticeDataText).focus(); }).change(); } }, -- cgit v1.2.3-54-g00ecf From a35c52b6bef775a47785922a25fad3d9199fce1c Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Mon, 18 Jan 2010 12:55:14 +0000 Subject: 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 a10e9d15a..aeec8d89d 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(); -- cgit v1.2.3-54-g00ecf From a7a0041a1157ad3ceec2ebd6af722a2529ad422f Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Fri, 22 Jan 2010 00:27:08 +0000 Subject: 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 aeec8d89d..a7339010a 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); } }); -- cgit v1.2.3-54-g00ecf From 591c5037a745f1b620b213ddaa84c82c64201b71 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Mon, 25 Jan 2010 14:55:04 +0000 Subject: 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 a7339010a..8d52d859b 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; } }, -- cgit v1.2.3-54-g00ecf From e9a155db733f5266816ae5904db1eace4af1e2df Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Sat, 16 Jan 2010 20:57:18 +0000 Subject: 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 2e4c88dfa..893cc60b5 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; -- cgit v1.2.3-54-g00ecf From 83eef607f1f01256e7342b421bddbed1fc32d6a6 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Sun, 17 Jan 2010 14:04:47 +0000 Subject: 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 14d2500e8..d426fc282 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); -- cgit v1.2.3-54-g00ecf From 14c3ec73b93ad59d5eb3947ac5a4b7ed6c26daad Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Sun, 17 Jan 2010 19:45:35 +0000 Subject: 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 3eefc0c8e..472fbb001 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; -- cgit v1.2.3-54-g00ecf From a93a3cc2701a0a948fe06b1d2a156c5d6b28c006 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Mon, 18 Jan 2010 11:12:05 +0000 Subject: Moved farbtastic's stylesheet to use relative paths for its own images --- js/farbtastic/farbtastic.css | 32 ++++++++++++++++++++++++++++++++ theme/base/css/farbtastic.css | 32 -------------------------------- 2 files changed, 32 insertions(+), 32 deletions(-) create mode 100644 js/farbtastic/farbtastic.css delete mode 100644 theme/base/css/farbtastic.css diff --git a/js/farbtastic/farbtastic.css b/js/farbtastic/farbtastic.css new file mode 100644 index 000000000..a88e7b868 --- /dev/null +++ b/js/farbtastic/farbtastic.css @@ -0,0 +1,32 @@ +.farbtastic { + position: relative; +} +.farbtastic * { + position: absolute; + cursor: crosshair; +} +.farbtastic, .farbtastic .wheel { + width: 195px; + height: 195px; +} +.farbtastic .color, .farbtastic .overlay { + top: 47px; + left: 47px; + width: 101px; + height: 101px; +} +.farbtastic .wheel { + background: url(wheel.png) no-repeat; + width: 195px; + height: 195px; +} +.farbtastic .overlay { + background: url(mask.png) no-repeat; +} +.farbtastic .marker { + width: 17px; + height: 17px; + margin: -8px 0 0 -8px; + overflow: hidden; + background: url(marker.png) no-repeat; +} diff --git a/theme/base/css/farbtastic.css b/theme/base/css/farbtastic.css deleted file mode 100644 index 7efcc73c3..000000000 --- a/theme/base/css/farbtastic.css +++ /dev/null @@ -1,32 +0,0 @@ -.farbtastic { - position: relative; -} -.farbtastic * { - position: absolute; - cursor: crosshair; -} -.farbtastic, .farbtastic .wheel { - width: 195px; - height: 195px; -} -.farbtastic .color, .farbtastic .overlay { - top: 47px; - left: 47px; - width: 101px; - height: 101px; -} -.farbtastic .wheel { - background: url(../../../js/farbtastic/wheel.png) no-repeat; - width: 195px; - height: 195px; -} -.farbtastic .overlay { - background: url(../../../js/farbtastic/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; -} -- cgit v1.2.3-54-g00ecf From b1a4ccb4dfb0e815f4955ef0c5cde27d85437eb7 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Mon, 18 Jan 2010 11:29:05 +0000 Subject: 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 f862aff0e..72ad6ade2 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 b70ba0dfc..8e44c03a9 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'); } /** -- cgit v1.2.3-54-g00ecf From dbb44ca39a84181fa8ccfb66657075250507945c Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Mon, 18 Jan 2010 17:17:02 +0000 Subject: 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 bfef2970d..08608348c 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); -- cgit v1.2.3-54-g00ecf From 1406cc452f212f629bdba8ac3e4422e907fbfb7f Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Wed, 20 Jan 2010 18:32:24 +0100 Subject: 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 472fbb001..76071352d 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 893cc60b5..7eff4709c 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; } -- cgit v1.2.3-54-g00ecf From 2f74a9bf148a22bb19524da290783cc0c10d38ba Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Wed, 20 Jan 2010 18:50:48 +0100 Subject: 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 76071352d..04fa5fb00 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; } -- cgit v1.2.3-54-g00ecf From 72ae0ecc1c5e021b0f562fe5aa7ad0140d9c9ecf Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 21 Jan 2010 13:23:04 +0100 Subject: 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 171bea17c..e9207a66a 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 f6c53ea8d..000000000 --- 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; -} -- cgit v1.2.3-54-g00ecf From 6ae453bb7d7db232bead11c833d9447ee4e4ec0b Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Fri, 22 Jan 2010 19:18:14 +0100 Subject: 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 8276f22a1..e43d1c43c 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(); }, -- cgit v1.2.3-54-g00ecf From 23e96dd4d1f2378a62dc24f2f11b3e8b645bac1c Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Sun, 24 Jan 2010 15:34:40 +0100 Subject: 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 d426fc282..5c913836d 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; + } +} -- cgit v1.2.3-54-g00ecf From 9bacdb9134b39f97fd244bdc28d9e467ac05b61d Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Mon, 25 Jan 2010 22:44:05 +0100 Subject: Updated howto create a theme --- theme/README | 51 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/theme/README b/theme/README index 266a89fdf..e154a723c 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. + -- cgit v1.2.3-54-g00ecf From 817f01c3b18ec626701de2a1402562eeb87eee6d Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Tue, 26 Jan 2010 01:58:10 +0100 Subject: 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 8d52d859b..b864867fd 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; } -- cgit v1.2.3-54-g00ecf From cebab436a16b392d3854d6abe652a063e055b8c7 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Tue, 26 Jan 2010 19:13:05 +0100 Subject: 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 56f869354..31e7c2ae6 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; } -- cgit v1.2.3-54-g00ecf From 2e0297bb6ff0fd2d3a37d811bd3305ce5d6a77ee Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Wed, 27 Jan 2010 15:08:33 +0000 Subject: 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 7eff4709c..d4c471674 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 { -- cgit v1.2.3-54-g00ecf From b1402896e7dac59ab1eecae4babf83a06d2f256d Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 27 Jan 2010 09:13:21 -0800 Subject: 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 8c8b8da6d..2bc4b892b 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)); -- cgit v1.2.3-54-g00ecf From 00d9b215f4de97f94035880041179879fcf0d201 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Wed, 27 Jan 2010 17:29:36 -0500 Subject: 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 000000000..dc2b32bc8 --- /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 -- cgit v1.2.3-54-g00ecf From 2aba2eeeaf78f4e407abe23643ff6027b0ea53dd Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Wed, 27 Jan 2010 17:55:33 -0500 Subject: 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 dc2b32bc8..ab2b9a6fb 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); -- cgit v1.2.3-54-g00ecf From 06cd3358970bfc27ff027cd1d8dc6974950c3793 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 27 Jan 2010 10:08:24 -0800 Subject: 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 000000000..436e085be --- /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 8d03b06f5..a0f10b352 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 777711fb5..d468df3ae 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 -- cgit v1.2.3-54-g00ecf From 0373ab6fa437309ab790d5de4d43e241ba510c93 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Wed, 27 Jan 2010 18:35:02 -0500 Subject: 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 000000000..a0d02ec11 --- /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 -- cgit v1.2.3-54-g00ecf From e7a5471287598902d4fefbb93f1247d8c025e66b Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 27 Jan 2010 15:37:18 -0800 Subject: 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 a0f10b352..f247a3bca 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 d468df3ae..ae392191f 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 -- cgit v1.2.3-54-g00ecf From 97a1ef14d2bfd32ec14db57b56d861e9cad7d02d Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Wed, 27 Jan 2010 18:39:55 -0500 Subject: 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 a0d02ec11..96ed82b44 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 -- cgit v1.2.3-54-g00ecf From dd413ff4faeada13571711a0e4cc51e5ab25f7cc Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Wed, 27 Jan 2010 18:44:46 -0500 Subject: 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 96ed82b44..59485f25d 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 -- cgit v1.2.3-54-g00ecf From fe531dfc6c9c700b5566db634a9078d4b4b87d87 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 27 Jan 2010 16:03:01 -0800 Subject: 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 ae392191f..f502a169a 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" -- cgit v1.2.3-54-g00ecf From 420ae06faf033b799c677845045b50b259801dbd Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Thu, 28 Jan 2010 00:39:40 +0000 Subject: 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 1a5afd552..f19e315bf 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 c040b9f6a..ca62b5f51 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 73ecc9249..f29e63713 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 69ead0b53..97f843561 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 7aa49b1bf..95d6f95af 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 7b4017531..d0e9e4926 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 ab96f2e5f..dc1ab8685 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 5109cd806..d09480759 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 a7fe0dcc1..6c8fad49b 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; + } + } -- cgit v1.2.3-54-g00ecf From 324590c46eba2900164d0487a2d525ea4e9f93b8 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Thu, 28 Jan 2010 00:41:44 +0000 Subject: 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 ac5e997c7..d441014ad 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; } -- cgit v1.2.3-54-g00ecf From f296f04abdc545f03daf55676282a2ae7108f79e Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Thu, 28 Jan 2010 01:24:40 +0000 Subject: Remove debugging statement --- lib/apiauth.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/apiauth.php b/lib/apiauth.php index d441014ad..c684a6cae 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.'); -- cgit v1.2.3-54-g00ecf From 07d50a012af70d9bd35a26f645d791ae7ba29986 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 27 Jan 2010 17:34:13 -0800 Subject: 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 8342c4bc6..917c1f1c4 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'; -- cgit v1.2.3-54-g00ecf From 5182cc686dacfcf64130b591a3b7ded8d4a0c9dc Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Thu, 28 Jan 2010 01:39:18 +0000 Subject: 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 fa074c4e7..15c3a9dad 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, -- cgit v1.2.3-54-g00ecf From 3abfb454a381806a0f237e1463d2ba9a8612c22a Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 27 Jan 2010 18:39:17 -0800 Subject: 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 0966697e2..6b364fb5c 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 10ea34864..c729193b5 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' -- cgit v1.2.3-54-g00ecf From c805a5e51874aa231c728c14402542648edb24bf Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 27 Jan 2010 18:55:22 -0800 Subject: Update queue, translation notes in readme --- README | 39 +++++++++++++++++---------------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/README b/README index f83873ca8..da278f741 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. - -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', + them as notices in the database; also pulls queued XMPP output from + queuedaemon.php to push out to clients. + +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 ------- -- cgit v1.2.3-54-g00ecf From ee4ea3f3e1eb64df2dd3ba677f5201f8787482a8 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Wed, 27 Jan 2010 21:59:38 -0500 Subject: 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 ada48b339..b4e4a653c 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'); -- cgit v1.2.3-54-g00ecf From 5c0560a7fc7d5e7bb9c91da7cc273d2dc89e32b2 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 27 Jan 2010 19:50:08 -0800 Subject: 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 16e28e94d..6c212453e 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) -- cgit v1.2.3-54-g00ecf From f026ecec322e59899b5ce87abaa6d93738f67b9e Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 27 Jan 2010 19:50:08 -0800 Subject: 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 16e28e94d..6c212453e 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) -- cgit v1.2.3-54-g00ecf From fbd52111e1cc68a1a2710701ae0a146bcce5580e Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 27 Jan 2010 19:58:33 -0800 Subject: 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 6b364fb5c..90e3e76ef 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -1378,8 +1378,6 @@ class Notice extends Memcached_DataObject } $reply->free(); - - return $ids; } function clearRepeats() -- cgit v1.2.3-54-g00ecf From 644c319f5ad78dbedb686cdfef38152cadf615b2 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 27 Jan 2010 19:58:33 -0800 Subject: 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 6b364fb5c..90e3e76ef 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -1378,8 +1378,6 @@ class Notice extends Memcached_DataObject } $reply->free(); - - return $ids; } function clearRepeats() -- cgit v1.2.3-54-g00ecf From c2c262e4b47f5a4965317148b66ff390f4fc85fd Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Thu, 28 Jan 2010 04:46:10 +0000 Subject: Move faceboookapp.js to the Facebook plugin --- js/facebookapp.js | 33 --------------------------------- plugins/Facebook/facebookaction.php | 4 +--- plugins/Facebook/facebookapp.js | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 36 deletions(-) delete mode 100644 js/facebookapp.js create mode 100644 plugins/Facebook/facebookapp.js diff --git a/js/facebookapp.js b/js/facebookapp.js deleted file mode 100644 index 5deb6e42b..000000000 --- a/js/facebookapp.js +++ /dev/null @@ -1,33 +0,0 @@ -/* -* StatusNet - a distributed open-source microblogging tool -* Copyright (C) 2008, StatusNet, Inc. -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU Affero General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Affero General Public License for more details. -* -* You should have received a copy of the GNU Affero General Public License -* along with this program. If not, see . -*/ - -var max = 140; -var noticeBox = document.getElementById('notice_data-text'); - -if (noticeBox) { - noticeBox.addEventListener('keyup', keypress); - noticeBox.addEventListener('keydown', keypress); - noticeBox.addEventListener('keypress', keypress); - noticeBox.addEventListener('change', keypress); -} - -// Do our the countdown -function keypress(evt) { - document.getElementById('notice_text-count').setTextValue( - max - noticeBox.getValue().length); -} diff --git a/plugins/Facebook/facebookaction.php b/plugins/Facebook/facebookaction.php index 389e1ea81..8437a705a 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/plugins/Facebook/facebookapp.js b/plugins/Facebook/facebookapp.js new file mode 100644 index 000000000..5deb6e42b --- /dev/null +++ b/plugins/Facebook/facebookapp.js @@ -0,0 +1,33 @@ +/* +* StatusNet - a distributed open-source microblogging tool +* Copyright (C) 2008, StatusNet, Inc. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Affero General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Affero General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with this program. If not, see . +*/ + +var max = 140; +var noticeBox = document.getElementById('notice_data-text'); + +if (noticeBox) { + noticeBox.addEventListener('keyup', keypress); + noticeBox.addEventListener('keydown', keypress); + noticeBox.addEventListener('keypress', keypress); + noticeBox.addEventListener('change', keypress); +} + +// Do our the countdown +function keypress(evt) { + document.getElementById('notice_text-count').setTextValue( + max - noticeBox.getValue().length); +} -- cgit v1.2.3-54-g00ecf From 427ac3a3a6eafa9a82006e9f0a9c2ab19a09fb4b Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 27 Jan 2010 20:51:04 -0800 Subject: 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 19e8c49b5..da70d9ae3 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) { -- cgit v1.2.3-54-g00ecf From 7347381183bfee96db8b2f89a4ba0ce5d04f76e2 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 27 Jan 2010 21:42:13 -0800 Subject: 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 b4e4a653c..b482464aa 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); -- cgit v1.2.3-54-g00ecf From 1ba8045a9b318fd19fb55d2c469bd23a3976c2e8 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 28 Jan 2010 01:24:00 -0500 Subject: 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 4312f9876..c6dc4b43a 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'])) { -- cgit v1.2.3-54-g00ecf From 65c4cff01c42a5c432db968cf6731104b8040134 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 27 Jan 2010 23:14:49 -0800 Subject: 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 c6dc4b43a..26a12871b 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'])) { -- cgit v1.2.3-54-g00ecf From 78fe76b058157670bf6c0ab5f3454733d465684e Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 27 Jan 2010 23:16:06 -0800 Subject: 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 26a12871b..dd8189a58 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'])) { -- cgit v1.2.3-54-g00ecf From ffaaf9de4a1da25f6168c53a33b25683ae134c61 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 27 Jan 2010 23:51:22 -0800 Subject: 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 2cc6377f8..b60aa7911 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(); -- cgit v1.2.3-54-g00ecf From dcce323d18d4b779d2456f32aa99dfac5e2b7520 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 28 Jan 2010 15:05:23 +0100 Subject: 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 78abf34a7..85c169716 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'); -- cgit v1.2.3-54-g00ecf From 5b1245a32a04de602196d2216b9d25ac32e0fd3b Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 28 Jan 2010 15:06:03 +0100 Subject: 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 90ff67073..c52919386 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)); -- cgit v1.2.3-54-g00ecf From c6f8b94fa91e3e5559a9b79766e7320b096b839e Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 28 Jan 2010 16:02:39 +0100 Subject: 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 65dd15990..3c51deb31 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 3aebb239d..06711850f 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 2818196c2..1ac96ab5b 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 { -- cgit v1.2.3-54-g00ecf From 61114ceff7c4124519cb5aa59b4d06ec73127e9d Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 28 Jan 2010 16:39:20 +0100 Subject: 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 6357e55b4..471ac580d 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%; -- cgit v1.2.3-54-g00ecf From d4289cb34e9aefe7ffea3ca506ea0223f7858cb6 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 28 Jan 2010 16:41:28 +0100 Subject: 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 471ac580d..2d4ac85ba 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; -- cgit v1.2.3-54-g00ecf From b969fba2f5de4edf43e514e23565e1a90f916d04 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 28 Jan 2010 16:49:33 +0100 Subject: 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 2d4ac85ba..47845421a 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 7ea451576..7a53b02bf 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); -- cgit v1.2.3-54-g00ecf From e881888789be64576cb299611c80a0d191fa5682 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 28 Jan 2010 17:08:24 +0100 Subject: 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 47845421a..4ce7b49ca 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 7a53b02bf..4dfd25a99 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%; } -- cgit v1.2.3-54-g00ecf From 6e556e502a13a1cfef51492707932635322f4bc4 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 28 Jan 2010 17:09:35 +0100 Subject: 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 4ce7b49ca..ec8ca22f5 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; -- cgit v1.2.3-54-g00ecf From 83087e9d9b14b4425368dbd2f95ff6fcc909e7f6 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 28 Jan 2010 17:12:48 +0100 Subject: 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 ec8ca22f5..d5873b0b0 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; -- cgit v1.2.3-54-g00ecf From 156efda37a2a6d2e16b061180fea828581691560 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 28 Jan 2010 17:16:11 +0100 Subject: Updated biz theme to use dialogbox styles --- theme/biz/css/base.css | 43 +++++++++++++++++++++++++++++++++++++++++++ theme/biz/css/display.css | 40 +++++++++++++++++++++++++--------------- 2 files changed, 68 insertions(+), 15 deletions(-) diff --git a/theme/biz/css/base.css b/theme/biz/css/base.css index d5873b0b0..a8834ca57 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 4dfd25a99..52f36ab54 100644 --- a/theme/biz/css/display.css +++ b/theme/biz/css/display.css @@ -40,24 +40,34 @@ border-color:#DDDDDD; background:none; } -input.submit, -#form_notice.warning #notice_text-count, -.form_settings .form_note, -.entity_remote_subscribe { -background-color:#9BB43E; +input.submit { +color:#FFFFFF; } - -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); +.entity_actions input.submit { +border-color:transparent; +text-shadow:none; } +.dialogbox .submit_dialogbox, input.submit, -.entity_remote_subscribe, -#site_nav_local_views a { -color:#FFFFFF; +.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] { -- cgit v1.2.3-54-g00ecf From d955fb5e374d94526f9af928d28514e7b9a27a8f Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 28 Jan 2010 17:22:01 +0100 Subject: Updated biz theme entity_actions styles --- theme/biz/css/base.css | 102 +++++++++++++++++++++++++++++++++++----------- theme/biz/css/display.css | 41 ++++++++++--------- 2 files changed, 100 insertions(+), 43 deletions(-) diff --git a/theme/biz/css/base.css b/theme/biz/css/base.css index a8834ca57..bd70c083e 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,40 +634,95 @@ 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; } +.entity_actions a, +.entity_actions input { +border-radius:4px; +-moz-border-radius:4px; +-webkit-border-radius:4px; +} -.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_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_edit a, -.entity_send-a-message a, -.entity_nudge p { -padding:4px 4px 4px 23px; +.entity_actions .accept { +margin-bottom:18px; } -.entity_remote_subscribe { -padding:4px; -border-width:2px; +.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_actions .accept { -margin-bottom:18px; +.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 { diff --git a/theme/biz/css/display.css b/theme/biz/css/display.css index 52f36ab54..7fd78470f 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; -- cgit v1.2.3-54-g00ecf From d29af38a0be2f0944297898b166e86d0cebd9bd2 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 28 Jan 2010 17:28:11 +0100 Subject: 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 7fd78470f..0b7c17de7 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, -- cgit v1.2.3-54-g00ecf From 1662aa11139517cccc600d65b9e8eba07c541ea7 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 28 Jan 2010 17:32:24 +0100 Subject: 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 bd70c083e..8a34425be 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 0b7c17de7..7768d5146 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; } -- cgit v1.2.3-54-g00ecf From b4babedd29661a15a8dd135a20ffaf7059b7fa50 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 28 Jan 2010 17:36:33 +0100 Subject: 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 8a34425be..366339db2 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 { -- cgit v1.2.3-54-g00ecf From 22d9c32e7e755c36817912cb9e20554b23d211d4 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 28 Jan 2010 18:39:30 +0100 Subject: 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 366339db2..2c2ab33a0 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 7768d5146..f133ac30b 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; } -- cgit v1.2.3-54-g00ecf From a868a523a5ab042e75d333298a75aaa369d445cc Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Thu, 28 Jan 2010 09:52:35 -0800 Subject: 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 c729193b5..8de8b1097 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 da70d9ae3..4e2b58602 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"); -- cgit v1.2.3-54-g00ecf From 558bf0f48907ad463872075c778c12bc5821f510 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Thu, 28 Jan 2010 18:11:44 +0000 Subject: 'Sign in with Twitter' button img --- plugins/TwitterBridge/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 000000000..297bb0340 Binary files /dev/null and b/plugins/TwitterBridge/Sign-in-with-Twitter-lighter.png differ -- cgit v1.2.3-54-g00ecf From d773ed8193ee63db360386507e0511d1742b2dd1 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Thu, 28 Jan 2010 18:34:25 +0000 Subject: 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 6f03a9bed..9b7d05861 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')); -- cgit v1.2.3-54-g00ecf From 612dce4fe12e20643c2d33910c2fdf361a4845a5 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 28 Jan 2010 16:39:20 +0100 Subject: 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 6357e55b4..471ac580d 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%; -- cgit v1.2.3-54-g00ecf From df2390a90f43e7493eef492960526df112efa9a4 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 28 Jan 2010 16:41:28 +0100 Subject: 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 471ac580d..2d4ac85ba 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; -- cgit v1.2.3-54-g00ecf From 108aa050af17639c319305338ea9de9e28847275 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 28 Jan 2010 16:49:33 +0100 Subject: 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 2d4ac85ba..47845421a 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 7ea451576..7a53b02bf 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); -- cgit v1.2.3-54-g00ecf From f4c037f956ac7ca638c3d800f2d9070aa31d45e0 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 28 Jan 2010 17:08:24 +0100 Subject: 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 47845421a..4ce7b49ca 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 7a53b02bf..4dfd25a99 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%; } -- cgit v1.2.3-54-g00ecf From 8a18fd9591cfc8772d58b14f8a532558e70911f6 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 28 Jan 2010 17:09:35 +0100 Subject: 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 4ce7b49ca..ec8ca22f5 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; -- cgit v1.2.3-54-g00ecf From 852a8b8295066140d4745b8a92eb835e8be6c3b0 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 28 Jan 2010 17:12:48 +0100 Subject: 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 ec8ca22f5..d5873b0b0 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; -- cgit v1.2.3-54-g00ecf From 9af751c03a08445b7e1fb79af60a60c65a81f89f Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 28 Jan 2010 17:16:11 +0100 Subject: Updated biz theme to use dialogbox styles --- theme/biz/css/base.css | 43 +++++++++++++++++++++++++++++++++++++++++++ theme/biz/css/display.css | 40 +++++++++++++++++++++++++--------------- 2 files changed, 68 insertions(+), 15 deletions(-) diff --git a/theme/biz/css/base.css b/theme/biz/css/base.css index d5873b0b0..a8834ca57 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 4dfd25a99..52f36ab54 100644 --- a/theme/biz/css/display.css +++ b/theme/biz/css/display.css @@ -40,24 +40,34 @@ border-color:#DDDDDD; background:none; } -input.submit, -#form_notice.warning #notice_text-count, -.form_settings .form_note, -.entity_remote_subscribe { -background-color:#9BB43E; +input.submit { +color:#FFFFFF; } - -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); +.entity_actions input.submit { +border-color:transparent; +text-shadow:none; } +.dialogbox .submit_dialogbox, input.submit, -.entity_remote_subscribe, -#site_nav_local_views a { -color:#FFFFFF; +.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] { -- cgit v1.2.3-54-g00ecf From f66e0ed1392a01e925e3d5dcc1772d32cad6c271 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 28 Jan 2010 17:22:01 +0100 Subject: Updated biz theme entity_actions styles --- theme/biz/css/base.css | 102 +++++++++++++++++++++++++++++++++++----------- theme/biz/css/display.css | 41 ++++++++++--------- 2 files changed, 100 insertions(+), 43 deletions(-) diff --git a/theme/biz/css/base.css b/theme/biz/css/base.css index a8834ca57..bd70c083e 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,40 +634,95 @@ 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; } +.entity_actions a, +.entity_actions input { +border-radius:4px; +-moz-border-radius:4px; +-webkit-border-radius:4px; +} -.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_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_edit a, -.entity_send-a-message a, -.entity_nudge p { -padding:4px 4px 4px 23px; +.entity_actions .accept { +margin-bottom:18px; } -.entity_remote_subscribe { -padding:4px; -border-width:2px; +.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_actions .accept { -margin-bottom:18px; +.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 { diff --git a/theme/biz/css/display.css b/theme/biz/css/display.css index 52f36ab54..7fd78470f 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; -- cgit v1.2.3-54-g00ecf From 72fc0f6b8a4ad392142c64f075dd161a50e72c7c Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 28 Jan 2010 17:28:11 +0100 Subject: 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 7fd78470f..0b7c17de7 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, -- cgit v1.2.3-54-g00ecf From 8eec008b0ce96408d295745fe1c0ea66f876de34 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 28 Jan 2010 17:32:24 +0100 Subject: 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 bd70c083e..8a34425be 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 0b7c17de7..7768d5146 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; } -- cgit v1.2.3-54-g00ecf From 1e8b14d24b8ef748b4451322b7ff68aaa1f00eae Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 28 Jan 2010 17:36:33 +0100 Subject: 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 8a34425be..366339db2 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 { -- cgit v1.2.3-54-g00ecf From 171c97f17eecb3165d6ac088fece15a56f7c9914 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 28 Jan 2010 18:39:30 +0100 Subject: 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 366339db2..2c2ab33a0 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 7768d5146..f133ac30b 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; } -- cgit v1.2.3-54-g00ecf From e5ff610e755e205f06dbe5ada20fcfac2f2bb669 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Thu, 28 Jan 2010 18:11:44 +0000 Subject: 'Sign in with Twitter' button img --- plugins/TwitterBridge/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 000000000..297bb0340 Binary files /dev/null and b/plugins/TwitterBridge/Sign-in-with-Twitter-lighter.png differ -- cgit v1.2.3-54-g00ecf From c81318d3ca60f47b3e248c64b7c27364a24814a7 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 28 Jan 2010 13:53:28 -0500 Subject: 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 79a69a96e..2422f8b68 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); } } -- cgit v1.2.3-54-g00ecf From 84b5480007d30280cc9c829fe1316db0f853f64c Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 28 Jan 2010 12:57:52 -0500 Subject: 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 2cc6377f8..bc9aaf81f 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)); + } } -- cgit v1.2.3-54-g00ecf From 74a03cbe1fecda9764f826c088331cc4ffbb9433 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 28 Jan 2010 14:27:35 -0500 Subject: 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 b5edc0f94..5520d690b 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; -- cgit v1.2.3-54-g00ecf From 513f8be07a22d722b86509e570bee46d028066f2 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 28 Jan 2010 16:26:55 -0500 Subject: 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 e615f2353..f4dfe6314 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"); + } } -- cgit v1.2.3-54-g00ecf From fa7895333724e314e2b32cb89f19a41069c554be Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 28 Jan 2010 16:35:38 -0500 Subject: 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 5520d690b..5aa40440a 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(); -- cgit v1.2.3-54-g00ecf From be7bca2303cc9900f2c1a746a10a785d9d95783c Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 28 Jan 2010 16:50:28 -0500 Subject: 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 5aa40440a..5520d690b 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(); -- cgit v1.2.3-54-g00ecf From a33194effb350a03dcdf1c0683fb15d575d245e5 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 28 Jan 2010 16:52:05 -0500 Subject: 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 5520d690b..5aa40440a 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(); -- cgit v1.2.3-54-g00ecf From 63a0e84a8b94d84b106431b648ec76e2537ab9c6 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 28 Jan 2010 16:52:42 -0500 Subject: 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 5aa40440a..605b380bf 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 -- cgit v1.2.3-54-g00ecf From 155a5d446f96651abf3eb62f9b5748e4bdfa0a76 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Thu, 28 Jan 2010 16:49:32 -0800 Subject: 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 8de8b1097..c01508695 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 c9233843a..3d38953fd 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 4e2b58602..ec150bbb6 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; } } } -- cgit v1.2.3-54-g00ecf From d13d73c5630244963f0c3bd9db68dd6c6451821a Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 28 Jan 2010 18:40:38 -0500 Subject: 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 90e3e76ef..a60dd5bcd 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); + } + } } -- cgit v1.2.3-54-g00ecf From 48a1a5a2dcaf026d92caf0656d44f324cd1bbf0c Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Fri, 29 Jan 2010 01:49:38 +0000 Subject: 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 c684a6cae..99500404f 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'); - if (empty($app)) { + $app = Oauth_application::getByConsumerKey($consumer); - // this should probably not happen + if (empty($app)) { 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.'); } -- cgit v1.2.3-54-g00ecf From 4a0413c0270bcac456c20972342c2c29182bec4e Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 28 Jan 2010 01:42:52 -0500 Subject: 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 000000000..1d7b60b90 --- /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); + } + } +} -- cgit v1.2.3-54-g00ecf From 864ce8e276220262ef8a26a9138c929145ccf57e Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Thu, 28 Jan 2010 20:09:17 -0800 Subject: 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 8123265e4..adb71cba7 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 1d7b60b90..e91d5eb50 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 -- cgit v1.2.3-54-g00ecf From 440ee00b1ed9c1b0b552c14b72c962a79e1a402e Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Thu, 28 Jan 2010 22:04:14 -0800 Subject: 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 000000000..4386ef844 --- /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 5de2db5ff..6813222f5 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 f62bfa458..f05627b31 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 c01508695..1337a9633 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 03765b39d..be9cfac0c 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'), -- cgit v1.2.3-54-g00ecf From 288dc3452f3c274d2165d8e7d502631a6eacc97c Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Thu, 28 Jan 2010 22:05:14 -0800 Subject: 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 c2e2351c3..30a8a9602 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'); -- cgit v1.2.3-54-g00ecf From ccb678ad15ee57302c751ea995264415c64ad298 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Thu, 28 Jan 2010 22:26:58 -0800 Subject: 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 f458d238d..4477468d0 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... + } } -- cgit v1.2.3-54-g00ecf From e5eca9bd2ce11633e14a840cb93adc6fd3ec8fc0 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Thu, 28 Jan 2010 22:51:07 -0800 Subject: 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 b6b23521b..e1bf06ba6 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); -- cgit v1.2.3-54-g00ecf From 4d3808a815dd8a020cf17151e4c04a821790169d Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Thu, 28 Jan 2010 23:08:36 -0800 Subject: 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 794b14050..10a2fae28 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 83471f2df..d6b4b7416 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 24896c784..1921c2bac 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 c9edb8d5d..a497d1385 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; } } -- cgit v1.2.3-54-g00ecf From efb39347b28e2c16fd297c61fae0606a28398dd7 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Fri, 29 Jan 2010 15:20:14 +0000 Subject: 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 3c51deb31..0d6395d05 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; -- cgit v1.2.3-54-g00ecf From f6c8b8a8680d99afe37accf8eda29e6660bf69ff Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Fri, 29 Jan 2010 15:20:14 +0000 Subject: 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 3c51deb31..0d6395d05 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; -- cgit v1.2.3-54-g00ecf From b68a21d4f77f7d05f12bf85068c2d0d1a0ba3a36 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Fri, 29 Jan 2010 15:43:37 +0000 Subject: 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 52151f9de..fb9dcdbfb 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']+""+ ""+ -- cgit v1.2.3-54-g00ecf From f6eecf02fc9eca0d3947a8cacf374909003dc8d4 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Fri, 29 Jan 2010 15:01:21 -0500 Subject: 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 f4dfe6314..ab65c30ce 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); + } } -- cgit v1.2.3-54-g00ecf From d437b76ed1a7ba3c39f0d3cb8bef15e19c1c184f Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Fri, 29 Jan 2010 15:15:04 -0500 Subject: 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 74aca3730..bf2c453ed 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'; -- cgit v1.2.3-54-g00ecf From 70a4f8c0e26bfdb76f595ce501c6e84a8011fea8 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Fri, 29 Jan 2010 15:15:23 -0500 Subject: 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 6ea975202..b70049617 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; + } } -- cgit v1.2.3-54-g00ecf From a7b2a08c42347d7beac43980a673b434a9c0331a Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Fri, 29 Jan 2010 15:15:52 -0500 Subject: 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 be9cfac0c..ca9f32812 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', -- cgit v1.2.3-54-g00ecf From 2a054a50fb404b01512a00872ab0f68481b0f470 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Fri, 29 Jan 2010 15:33:35 -0500 Subject: 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 f55f1486b..3a8ebdcfd 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 f502a169a..4ad808011 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' -- cgit v1.2.3-54-g00ecf From 8cb8b357a4383f7afb1e09fcd264aa41ac1502a6 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Fri, 29 Jan 2010 17:54:54 -0500 Subject: add hooks for user registration --- EVENTS.txt | 9 +++ classes/User.php | 171 +++++++++++++++++++++++++++++-------------------------- 2 files changed, 98 insertions(+), 82 deletions(-) diff --git a/EVENTS.txt b/EVENTS.txt index 1ed670697..3317c80de 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 b70049617..022044aac 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(); + if (empty($id)) { + common_log_db_error($profile, 'INSERT', __FILE__); + return false; + } - $inbox->user_id = $user->id; - $inbox->notice_ids = ''; + $user->id = $id; + $user->uri = common_user_uri($user); - $result = $inbox->insert(); + $result = $user->insert(); - if (!$result) { - common_log_db_error($inbox, 'INSERT', __FILE__); - return false; - } + if (!$result) { + common_log_db_error($user, 'INSERT', __FILE__); + return false; + } - // Everyone is subscribed to themself + // Everyone gets an inbox - $subscription = new Subscription(); - $subscription->subscriber = $user->id; - $subscription->subscribed = $user->id; - $subscription->created = $user->created; + $inbox = new Inbox(); - $result = $subscription->insert(); + $inbox->user_id = $user->id; + $inbox->notice_ids = ''; - 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 = $inbox->insert(); - $result = $confirm->insert(); if (!$result) { - common_log_db_error($confirm, 'INSERT', __FILE__); + common_log_db_error($inbox, 'INSERT', __FILE__); return false; } - } - if (!empty($code) && $user->email) { - $user->emailChanged(); - } + // Everyone is subscribed to themself - // Default system subscription + $subscription = new Subscription(); + $subscription->subscriber = $user->id; + $subscription->subscribed = $user->id; + $subscription->created = $user->created; - $defnick = common_config('newuser', 'default'); + $result = $subscription->insert(); - 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; + 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 = $defsub->insert(); + $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($code) && $user->email) { + $user->emailChanged(); + } - if (!empty($email) && !$user->email) { - mail_confirm_address($user, $confirm->code, $profile->nickname, $email); - } + // Default system subscription - // Welcome message + $defnick = common_config('newuser', 'default'); - $welcome = common_config('newuser', 'welcome'); + 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; - 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'); + $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; -- cgit v1.2.3-54-g00ecf From 8318f195a2997b4e3a4831d65685dca24a2b66aa Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Fri, 29 Jan 2010 18:29:51 -0500 Subject: 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 000000000..ab3187299 --- /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; + } +} -- cgit v1.2.3-54-g00ecf From e765a9657b0491c6d4fd6436c9cd489342a465c9 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sat, 30 Jan 2010 18:45:10 +1300 Subject: 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 000000000..1f7c3d092 --- /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 000000000..7291106dc --- /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 a7f64ebed..164387ff3 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; } -- cgit v1.2.3-54-g00ecf From 870c83c17de9710800163570cdc5321591e73c34 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sun, 10 Jan 2010 05:21:23 +0000 Subject: 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 164387ff3..c1636c21d 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; -- cgit v1.2.3-54-g00ecf From 22a6e46b45226647b6e7b9bfdc013e59c4a52428 Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Sat, 30 Jan 2010 21:22:30 +1300 Subject: 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 7291106dc..91bc09667 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 c1636c21d..27a4deda1 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. -- cgit v1.2.3-54-g00ecf From a1c9874a6119774a16917a631a7bb63f73a16ba1 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sat, 30 Jan 2010 12:40:11 -0500 Subject: 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 52cc9c97f..589462ed9 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($body)) { + throw new Exception("Empty HTTP body in response"); + } + + // This will throw an exception if the XML is mal-formed + + $document = new SimpleXMLElement($body); - if (empty($document)) { - throw new Exception("No results in response"); + // 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; + } } -- cgit v1.2.3-54-g00ecf From 1b7cc3393a6a039d4adcf955b3a79fe3c5cc6f47 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sat, 30 Jan 2010 12:43:00 -0500 Subject: 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 9671d2c27..7fd696baf 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; } -- cgit v1.2.3-54-g00ecf From def5d56ce1582c785cbff7d49103493b293838bb Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sat, 30 Jan 2010 12:47:21 -0500 Subject: 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 7fd696baf..e883c6ce4 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; } } -?> -- cgit v1.2.3-54-g00ecf From 4ae31f3476f30cf397c0d768119be3638be6fbf0 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sat, 30 Jan 2010 13:15:17 -0500 Subject: 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 ec150bbb6..6730cd213 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. -- cgit v1.2.3-54-g00ecf From fec8066bf76948142828b689708386861d089fb3 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sat, 30 Jan 2010 14:37:39 -0500 Subject: 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 a60dd5bcd..42878d94f 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; } -- cgit v1.2.3-54-g00ecf From dc62246443e3584ef5267505275f618f6fa86bf7 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sun, 31 Jan 2010 10:12:26 -0500 Subject: 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 3317c80de..6bf12bf13 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 da278f741..4e576dcdd 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 000000000..5131097c8 --- /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 605b380bf..06ff9900f 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 1337a9633..2bedc4bf0 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 ca9f32812..4b5b8d0bb 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', -- cgit v1.2.3-54-g00ecf From 30268cff7899519f249861e067e3af680ee1c570 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sun, 31 Jan 2010 15:16:59 -0500 Subject: 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 982dfde15..50278bfce 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 000000000..f88bf2e9a --- /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 c52919386..07cc68b76 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 4b5b8d0bb..b046b240c 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}')); -- cgit v1.2.3-54-g00ecf From 81087e45c5b797028e90181459e4c673cd7be278 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sun, 31 Jan 2010 15:25:59 -0500 Subject: move schema.type.php to typeschema.php like other files --- lib/mysqlschema.php | 537 +++++++++++++++++++++++++++++++++++++++++++++++++++ lib/pgsqlschema.php | 503 +++++++++++++++++++++++++++++++++++++++++++++++ lib/schema.mysql.php | 537 --------------------------------------------------- lib/schema.pgsql.php | 503 ----------------------------------------------- lib/schema.php | 8 +- 5 files changed, 1043 insertions(+), 1045 deletions(-) create mode 100644 lib/mysqlschema.php create mode 100644 lib/pgsqlschema.php delete mode 100644 lib/schema.mysql.php delete mode 100644 lib/schema.pgsql.php diff --git a/lib/mysqlschema.php b/lib/mysqlschema.php new file mode 100644 index 000000000..1f7c3d092 --- /dev/null +++ b/lib/mysqlschema.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/pgsqlschema.php b/lib/pgsqlschema.php new file mode 100644 index 000000000..91bc09667 --- /dev/null +++ b/lib/pgsqlschema.php @@ -0,0 +1,503 @@ +. + * + * @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("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()); + } + + $td = new TableDef(); + + $td->name = $name; + $td->columns = array(); + + $row = array(); + + while ($res->fetchInto($row, DB_FETCHMODE_ASSOC)) { +// var_dump($row); + $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.mysql.php b/lib/schema.mysql.php deleted file mode 100644 index 1f7c3d092..000000000 --- a/lib/schema.mysql.php +++ /dev/null @@ -1,537 +0,0 @@ -. - * - * @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 deleted file mode 100644 index 91bc09667..000000000 --- a/lib/schema.pgsql.php +++ /dev/null @@ -1,503 +0,0 @@ -. - * - * @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("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()); - } - - $td = new TableDef(); - - $td->name = $name; - $td->columns = array(); - - $row = array(); - - while ($res->fetchInto($row, DB_FETCHMODE_ASSOC)) { -// var_dump($row); - $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 27a4deda1..137b814e0 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 "; } -- cgit v1.2.3-54-g00ecf