1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace SunnySideUp\ShareThis; |
4
|
|
|
|
5
|
|
|
use HtmlEditorField; |
6
|
|
|
use SunnySideUp\ShareThis\FacebookFeed_Page; |
7
|
|
|
use SilverStripe\Security\Permission; |
8
|
|
|
use SilverStripe\Control\Director; |
9
|
|
|
use SilverStripe\Forms\LiteralField; |
10
|
|
|
use SilverStripe\Forms\TextField; |
11
|
|
|
use SilverStripe\Core\Injector\Injector; |
12
|
|
|
use SunnySideUp\ShareThis\RemoveFacebookItemController; |
13
|
|
|
use SilverStripe\ORM\DataObject; |
14
|
|
|
|
15
|
|
|
/** |
16
|
|
|
* FROM: http://www.acornartwork.com/blog/2010/04/19/tutorial-facebook-rss-feed-parser-in-pure-php/ |
17
|
|
|
* EXAMPLE: |
18
|
|
|
* //Run the function with the url and a number as arguments |
19
|
|
|
* $fb = new TheFaceBook_communicator(); |
20
|
|
|
* $dos = $fb->fetchFBFeed('http://facebook.com/feeds/status.php?id=xxxxxx&viewer=xxxxxx&key=xxxxx&format=rss20', 3); |
21
|
|
|
* //Print Facebook status updates |
22
|
|
|
* echo '<ul class="fb-updates">'; |
23
|
|
|
* foreach ($dos as $do) { |
24
|
|
|
* echo '<li>'; |
25
|
|
|
* echo '<span class="update">' .$do->Description. '</span>'; |
26
|
|
|
* echo '<span class="date">' .$do->Date. '</span>'; |
27
|
|
|
* echo '<span class="link"><a href="' .$do->Link. '">more</a></span>'; |
28
|
|
|
* echo '</li>'; |
29
|
|
|
* } |
30
|
|
|
* echo '</ul>'; |
31
|
|
|
* |
32
|
|
|
* SEE README on getting facebook URL for RSS Feed. |
33
|
|
|
* |
34
|
|
|
* |
35
|
|
|
**/ |
36
|
|
|
|
37
|
|
|
class FacebookFeed_Item extends DataObject |
38
|
|
|
{ |
39
|
|
|
private static $db = array( |
40
|
|
|
"Title" => "varchar(255)", |
41
|
|
|
"KeepOnTop" => "Boolean", |
42
|
|
|
"Hide" => "Boolean", |
43
|
|
|
"UID" => "varchar(32)", |
44
|
|
|
"Author" => "Varchar(244)", |
45
|
|
|
"Description" => "HTMLText", |
46
|
|
|
"DescriptionWithShortLink" => "HTMLText", |
47
|
|
|
"Link" => "Varchar(244)", |
48
|
|
|
"PictureLink" => "Text" |
49
|
|
|
); |
50
|
|
|
|
51
|
|
|
|
52
|
|
|
private static $summary_fields = array( |
53
|
|
|
"Created.Nice" => "Created", |
54
|
|
|
"FacebookFeed_Page.Title" => "Feed", |
55
|
|
|
"Title" => "Title", |
56
|
|
|
"KeepOnTopNice" => "Keep on top", |
57
|
|
|
"HideNice" => "Hide", |
58
|
|
|
); |
59
|
|
|
|
60
|
|
|
|
61
|
|
|
private static $has_one = array( |
62
|
|
|
"FacebookFeed_Page" => FacebookFeed_Page::class |
63
|
|
|
); |
64
|
|
|
|
65
|
|
|
private static $indexes = array( |
66
|
|
|
"UID" => true |
67
|
|
|
); |
68
|
|
|
|
69
|
|
|
private static $casting = array( |
70
|
|
|
'KeepOnTopNice' => 'Varchar', |
71
|
|
|
'HideNice' => 'Varchar', |
72
|
|
|
'FacebookPostLink' => 'Varchar' |
73
|
|
|
); |
74
|
|
|
|
75
|
|
|
private static $searchable_fields = array( |
76
|
|
|
'Title' => 'PartialMatchFilter', |
77
|
|
|
'Author' => 'PartialMatchFilter', |
78
|
|
|
'Description' => 'PartialMatchFilter', |
79
|
|
|
'Hide' => true, |
80
|
|
|
'KeepOnTop' => true |
81
|
|
|
); |
82
|
|
|
|
83
|
|
|
private static $default_sort = "\"Created\" DESC"; |
84
|
|
|
|
85
|
|
|
public function canCreate($member = null, $context = []) |
86
|
|
|
{ |
87
|
|
|
return false; |
88
|
|
|
} |
89
|
|
|
|
90
|
|
|
public function canView($member = null) |
|
|
|
|
91
|
|
|
{ |
92
|
|
|
return Permission::checkMember($member, 'SOCIAL_MEDIA'); |
93
|
|
|
} |
94
|
|
|
|
95
|
|
|
public function canEdit($member = null) |
|
|
|
|
96
|
|
|
{ |
97
|
|
|
return Permission::checkMember($member, 'SOCIAL_MEDIA'); |
98
|
|
|
} |
99
|
|
|
|
100
|
|
|
public function canDelete($member = null) |
101
|
|
|
{ |
102
|
|
|
return false; |
103
|
|
|
} |
104
|
|
|
|
105
|
|
|
private static $singular_name = "Facebook Item"; |
106
|
|
|
public function i18n_singular_name() |
107
|
|
|
{ |
108
|
|
|
return "Facebook Item"; |
109
|
|
|
} |
110
|
|
|
|
111
|
|
|
private static $plural_name = "Facebook Items"; |
112
|
|
|
public function i18n_plural_name() |
113
|
|
|
{ |
114
|
|
|
return "Facebook Items"; |
115
|
|
|
} |
116
|
|
|
|
117
|
|
|
public function onBeforeWrite() |
118
|
|
|
{ |
119
|
|
|
parent::onBeforeWrite(); |
120
|
|
|
$this->DescriptionWithShortLink = $this->Description; |
121
|
|
|
//$this->DescriptionWithShortLink = $this->createDescriptionWithShortLinks(); |
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
protected function createDescriptionWithShortLinks() |
125
|
|
|
{ |
126
|
|
|
require_once(Director::baseFolder()."/".SS_SHARETHIS_DIR.'/code/api/thirdparty/simple_html_dom.php'); |
127
|
|
|
$html = str_get_html($this->Description); |
128
|
|
|
if ($html) { |
129
|
|
|
foreach ($html->find('text') as $element) { |
130
|
|
|
//what exactly does it do? |
131
|
|
|
if (! in_array($element->parent()->tag, array('a', 'img'))) { |
132
|
|
|
$element->innertext = $this->replaceLinksWithProperOnes($element->innertext); |
133
|
|
|
} |
134
|
|
|
} |
135
|
|
|
} else { |
136
|
|
|
$this->Hide = true; |
137
|
|
|
$this->write(); |
138
|
|
|
} |
139
|
|
|
} |
140
|
|
|
|
141
|
|
|
protected function replaceLinksWithProperOnes($text) |
142
|
|
|
{ |
143
|
|
|
$rexProtocol = '(https?://)?'; |
144
|
|
|
$rexDomain = '((?:[-a-zA-Z0-9]{1,63}\.)+[-a-zA-Z0-9]{2,63}|(?:[0-9]{1,3}\.){3}[0-9]{1,3})'; |
145
|
|
|
$rexPort = '(:[0-9]{1,5})?'; |
146
|
|
|
$rexPath = '(/[!$-/0-9:;=@_\':;!a-zA-Z\x7f-\xff]*?)?'; |
147
|
|
|
$rexQuery = '(\?[!$-/0-9:;=@_\':;!a-zA-Z\x7f-\xff]+?)?'; |
148
|
|
|
$rexFragment = '(#[!$-/0-9:;=@_\':;!a-zA-Z\x7f-\xff]+?)?'; |
149
|
|
|
$outcome = ""; |
150
|
|
|
$validTlds = array_fill_keys(explode(" ", ".aero .asia .biz .cat .com .coop .edu .gov .info .int .jobs .mil .mobi .museum .name .net .org .pro .tel .travel .ac .ad .ae .af .ag .ai .al .am .an .ao .aq .ar .as .at .au .aw .ax .az .ba .bb .bd .be .bf .bg .bh .bi .bj .bm .bn .bo .br .bs .bt .bv .bw .by .bz .ca .cc .cd .cf .cg .ch .ci .ck .cl .cm .cn .co .cr .cu .cv .cx .cy .cz .de .dj .dk .dm .do .dz .ec .ee .eg .er .es .et .eu .fi .fj .fk .fm .fo .fr .ga .gb .gd .ge .gf .gg .gh .gi .gl .gm .gn .gp .gq .gr .gs .gt .gu .gw .gy .hk .hm .hn .hr .ht .hu .id .ie .il .im .in .io .iq .ir .is .it .je .jm .jo .jp .ke .kg .kh .ki .km .kn .kp .kr .kw .ky .kz .la .lb .lc .li .lk .lr .ls .lt .lu .lv .ly .ma .mc .md .me .mg .mh .mk .ml .mm .mn .mo .mp .mq .mr .ms .mt .mu .mv .mw .mx .my .mz .na .nc .ne .nf .ng .ni .nl .no .np .nr .nu .nz .om .pa .pe .pf .pg .ph .pk .pl .pm .pn .pr .ps .pt .pw .py .qa .re .ro .rs .ru .rw .sa .sb .sc .sd .se .sg .sh .si .sj .sk .sl .sm .sn .so .sr .st .su .sv .sy .sz .tc .td .tf .tg .th .tj .tk .tl .tm .tn .to .tp .tr .tt .tv .tw .tz .ua .ug .uk .us .uy .uz .va .vc .ve .vg .vi .vn .vu .wf .ws .ye .yt .yu .za .zm .zw .xn--0zwm56d .xn--11b5bs3a9aj6g .xn--80akhbyknj4f .xn--9t4b11yi5a .xn--deba0ad .xn--g6w251d .xn--hgbk6aj7f53bba .xn--hlcj6aya9esc7a .xn--jxalpdlp .xn--kgbechtv .xn--zckzah .arpa"), true); |
151
|
|
|
|
152
|
|
|
$position = 0; |
153
|
|
|
while (preg_match("{\\b$rexProtocol$rexDomain$rexPort$rexPath$rexQuery$rexFragment(?=[?.!,;:\"]?(\s|$))}", $text, $match, PREG_OFFSET_CAPTURE, $position)) { |
154
|
|
|
list($url, $urlPosition) = $match[0]; |
155
|
|
|
|
156
|
|
|
// Print the text leading up to the URL. |
157
|
|
|
$outcome .= (htmlspecialchars(substr($text, $position, $urlPosition - $position))); |
158
|
|
|
|
159
|
|
|
$domain = $match[2][0]; |
160
|
|
|
$port = $match[3][0]; |
161
|
|
|
$path = $match[4][0]; |
162
|
|
|
|
163
|
|
|
// Check if the TLD is valid - or that $domain is an IP address. |
164
|
|
|
$tld = strtolower(strrchr($domain, '.')); |
165
|
|
|
if (preg_match('{\.[0-9]{1,3}}', $tld) || isset($validTlds[$tld])) { |
166
|
|
|
// Prepend http:// if no protocol specified |
167
|
|
|
$completeUrl = $match[1][0] ? $url : "http://$url"; |
168
|
|
|
|
169
|
|
|
// Print the hyperlink. |
170
|
|
|
$outcome .= sprintf('<a href="%s">%s</a>', htmlspecialchars($completeUrl), htmlspecialchars("$domain$port$path")); |
171
|
|
|
} else { |
172
|
|
|
// Not a valid URL. |
173
|
|
|
$outcome .= (htmlspecialchars($url)); |
174
|
|
|
} |
175
|
|
|
|
176
|
|
|
// Continue text parsing from after the URL. |
177
|
|
|
$position = $urlPosition + strlen($url); |
178
|
|
|
} |
179
|
|
|
|
180
|
|
|
// Print the remainder of the text. |
181
|
|
|
$outcome .= ((substr($text, $position))); |
182
|
|
|
return $outcome; |
183
|
|
|
} |
184
|
|
|
|
185
|
|
|
public function getCMSFields() |
|
|
|
|
186
|
|
|
{ |
187
|
|
|
$fields = parent::getCMSFields(); |
188
|
|
|
$fields->removeByName("UID"); |
189
|
|
|
$fields->removeByName("PictureLink"); |
190
|
|
|
if ($this->PictureLink) { |
191
|
|
|
$fields->addFieldToTab("Root.Main", new LiteralField("PictureLinkIMG", "<img src=\"".$this->PictureLink."\" alt=\"\" />"), "Author"); |
192
|
|
|
} |
193
|
|
|
if ($this->Link) { |
194
|
|
|
$fields->addFieldToTab("Root.Main", new LiteralField("LinkLink", "<h2><a href=\"".$this->Link."\" >go to link final link: ".substr($this->Link, 0, 45)."...</a></h2>"), "Author"); |
195
|
|
|
$fields->addFieldToTab("Root.Main", new LiteralField("LinkLink", "<h2><a href=\"".$this->getFacebookPostLink()."\" >go to face book post: ".substr($this->getFacebookPostLink(), 0, 45)."...</a></h2>"), "Author"); |
196
|
|
|
$fields->addFieldToTab("Root.RawData", new TextField("Link", "Link")); |
197
|
|
|
} |
198
|
|
|
if ($this->Description) { |
199
|
|
|
$fields->addFieldToTab("Root.RawData", new HtmlEditorField("Description")); |
200
|
|
|
$fields->addFieldToTab("Root.Main", new HtmlEditorField("DescriptionWithShortLink", "Edited Description")); |
201
|
|
|
} |
202
|
|
|
return $fields; |
203
|
|
|
} |
204
|
|
|
|
205
|
|
|
public function KeepOnTopNice() |
|
|
|
|
206
|
|
|
{ |
207
|
|
|
return $this->dbObject('KeepOnTop')->Nice(); |
208
|
|
|
} |
209
|
|
|
|
210
|
|
|
public function HideNice() |
|
|
|
|
211
|
|
|
{ |
212
|
|
|
return $this->dbObject('Hide')->Nice(); |
213
|
|
|
} |
214
|
|
|
|
215
|
|
|
/** |
216
|
|
|
* @return string |
217
|
|
|
*/ |
218
|
|
|
public function getFacebookPostLink() |
219
|
|
|
{ |
220
|
|
|
return "https://facebook.com/" . $this->UID; |
221
|
|
|
} |
222
|
|
|
|
223
|
|
|
/** |
224
|
|
|
* is the link attached to the FB post a link back to this site? |
225
|
|
|
* |
226
|
|
|
* @return bool |
227
|
|
|
*/ |
228
|
|
|
public function IsLinkBackToSite() |
229
|
|
|
{ |
230
|
|
|
$currentURL = Director::absoluteBaseURL(); |
231
|
|
|
if (strpos($this->Link, $currentURL) === false) { |
|
|
|
|
232
|
|
|
return false; |
233
|
|
|
} |
234
|
|
|
return true; |
235
|
|
|
} |
236
|
|
|
|
237
|
|
|
/** |
238
|
|
|
* returns a link back to the same site if that is what the FB post links to |
239
|
|
|
* or a link to FB if it ultimately links to third-party site. |
240
|
|
|
* |
241
|
|
|
* @return strring |
242
|
|
|
*/ |
243
|
|
|
public function SmartLink() |
244
|
|
|
{ |
245
|
|
|
if ($this->IsLinkBackToSite()) { |
246
|
|
|
return $this->Link; |
247
|
|
|
} else { |
248
|
|
|
return $this->getFacebookPostLink(); |
249
|
|
|
} |
250
|
|
|
} |
251
|
|
|
|
252
|
|
|
public function fbpostExists() |
|
|
|
|
253
|
|
|
{ |
254
|
|
|
$exists = true; |
255
|
|
|
$handle = curl_init($this->getFacebookPostLink()); |
256
|
|
|
curl_setopt($handle, CURLOPT_RETURNTRANSFER, true); |
257
|
|
|
|
258
|
|
|
/* Get the HTML or whatever is linked in $url. */ |
259
|
|
|
$response = curl_exec($handle); |
|
|
|
|
260
|
|
|
|
261
|
|
|
/* Check for 404 (file not found). */ |
262
|
|
|
$httpCode = curl_getinfo($handle, CURLINFO_HTTP_CODE); |
263
|
|
|
if ($httpCode !== 200 && $httpCode !== 301) { |
264
|
|
|
$exists = false; |
265
|
|
|
} |
266
|
|
|
|
267
|
|
|
curl_close($handle); |
268
|
|
|
|
269
|
|
|
return $exists(); |
270
|
|
|
} |
271
|
|
|
|
272
|
|
|
public function canRemove() |
273
|
|
|
{ |
274
|
|
|
return Permission::check('CMS_ACCESS_CMSMain') ? true : false; |
275
|
|
|
} |
276
|
|
|
|
277
|
|
|
public function RemoveLink() |
|
|
|
|
278
|
|
|
{ |
279
|
|
|
$obj = Injector::inst()->get(RemoveFacebookItemController::class); |
280
|
|
|
|
281
|
|
|
return $obj->Link('remove/'.$this->UID.'/'); |
282
|
|
|
} |
283
|
|
|
} |
284
|
|
|
|
Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a
@return
annotation as described here.