code:
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-06 16:15+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Vietnamese (http://www.transifex.com/projects/p/gnu-social/language/vi/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: vi\n"
"Plural-Forms: nplurals=1; plural=0;\n"

#. TRANS: Link title for link on user profile.
#: actions/profiledetail.php:52
msgid "Edit extended profile settings"
msgstr ""

#. TRANS: Link text for link on user profile.
#: actions/profiledetail.php:54
msgid "Edit"
msgstr ""

#. TRANS: Title for extended profile settings.
#: actions/profiledetailsettings.php:29
msgid "Extended profile settings"
msgstr ""

#. TRANS: Usage instructions for profile settings.
#: actions/profiledetailsettings.php:40
msgid ""
"You can update your personal profile info here so people know more about "
"you."
msgstr "Bạn có thể cập nhật hồ sơ cá nhân tại đây để mọi người có thể biết thông tin về bạn."

#. TRANS: Client error displayed when the session token does not match or is
#. not given.
#: actions/profiledetailsettings.php:63
msgid "There was a problem with your session token. Try again, please."
msgstr "Có lỗi xảy ra khi thao tác. Hãy thử lại lần nữa."

#. TRANS: Message given submitting a form with an unknown action.
#: actions/profiledetailsettings.php:74
msgid "Unexpected form submission."
msgstr "Bất ngờ gửi mẫu thông tin. "

#. TRANS: Success message after saving extended profile details.
#: actions/profiledetailsettings.php:137
msgid "Details saved."
msgstr ""

#. TRANS: Exception thrown when no date was entered in a required date field.
#. TRANS: %s is the field name.
#: actions/profiledetailsettings.php:148
#, php-format
msgid "You must supply a date for \"%s\"."
msgstr ""

#. TRANS: Exception thrown on incorrect data input.
#. TRANS: %1$s is a field name, %2$s is the incorrect input.
#: actions/profiledetailsettings.php:160
#, php-format
msgid "Invalid date entered for \"%1$s\": %2$s."
msgstr ""

#. TRANS: Exception thrown when entering an invalid URL.
#. TRANS: %s is the invalid URL.
#: actions/profiledetailsettings.php:273
#, php-format
msgid "Invalid URL: %s."
msgstr ""

#. TRANS: Server error displayed when a field could not be saved in the
#. database.
#: actions/profiledetailsettings.php:522 actions/profiledetailsettings.php:535
msgid "Could not save profile details."
msgstr ""

#. TRANS: Validation error in form for profile settings.
#. TRANS: %s is an invalid tag.
#: actions/profiledetailsettings.php:580
#, php-format
msgid "Invalid tag: \"%s\"."
msgstr ""

#. TRANS: Server error thrown when user profile settings could not be saved.
#: actions/profiledetailsettings.php:622
msgid "Could not save profile."
msgstr ""

#. TRANS: Server error thrown when user profile settings tags could not be
#. saved.
#: actions/profiledetailsettings.php:630
msgid "Could not save tags."
msgstr ""

#. TRANS: Plugin description.
#: ExtendedProfilePlugin.php:40
msgid "UI extensions for additional profile fields."
msgstr ""

#. TRANS: Link text on user profile page leading to extended profile page.
#: ExtendedProfilePlugin.php:87
msgid "More details..."
msgstr ""

#. TRANS: Field label for extended profile properties.
#: lib/extendedprofile.php:120 lib/extendedprofile.php:131
msgid "Phone"
msgstr ""

#. TRANS: Field label for extended profile properties (Instant Messaging).
#: lib/extendedprofile.php:153 lib/extendedprofile.php:160
msgid "IM"
msgstr ""

#. TRANS: Field label for extended profile properties.
#: lib/extendedprofile.php:181 lib/extendedprofile.php:188
msgid "Website"
msgstr ""

#. TRANS: Field label for extended profile properties.
#: lib/extendedprofile.php:212 lib/extendedprofile.php:224
msgid "Employer"
msgstr ""

#. TRANS: Field label for extended profile properties.
#. TRANS: Field label in education area of extended profile.
#. TRANS: Field label in education edit area of extended profile.
#: lib/extendedprofile.php:251 lib/extendedprofile.php:264
#: lib/extendedprofilewidget.php:416 lib/extendedprofilewidget.php:456
msgid "Institution"
msgstr ""

#. TRANS: Field label for extended profile properties.
#: lib/extendedprofile.php:289 lib/extendedprofile.php:338
msgid "Personal"
msgstr ""

#. TRANS: Field label for extended profile properties.
#: lib/extendedprofile.php:293
msgid "Full name"
msgstr "Tên đầy đủ"

#. TRANS: Field label for extended profile properties.
#: lib/extendedprofile.php:299
msgid "Title"
msgstr ""

#. TRANS: Field label for extended profile properties.
#: lib/extendedprofile.php:304
msgid "Manager"
msgstr ""

#. TRANS: Field label for extended profile properties.
#: lib/extendedprofile.php:310
msgid "Location"
msgstr "Thành phố"

#. TRANS: Field label for extended profile properties.
#: lib/extendedprofile.php:315
msgid "Bio"
msgstr "Lý lịch"

#. TRANS: Field label for extended profile properties.
#: lib/extendedprofile.php:321
msgid "Tags"
msgstr "Từ khóa"

#. TRANS: Field label for extended profile properties.
#: lib/extendedprofile.php:329
msgid "Contact"
msgstr ""

#. TRANS: Field label for extended profile properties.
#: lib/extendedprofile.php:342
msgid "Birthday"
msgstr ""

#. TRANS: Field label for extended profile properties.
#: lib/extendedprofile.php:348
msgid "Spouse's name"
msgstr ""

#. TRANS: Field label for extended profile properties.
#: lib/extendedprofile.php:353
msgid "Kids' names"
msgstr ""

#. TRANS: Field label for extended profile properties.
#: lib/extendedprofile.php:359
msgid "Work experience"
msgstr ""

#. TRANS: Field label for extended profile properties.
#: lib/extendedprofile.php:366
msgid "Education"
msgstr ""

#. TRANS: Title for extended profile entry deletion dialog.
#: lib/extendedprofilewidget.php:84
msgid "Confirmation Required"
msgstr ""

#. TRANS: Confirmation text for extended profile entry deletion dialog.
#: lib/extendedprofilewidget.php:87
msgid "Really delete this entry?"
msgstr ""

#. TRANS: Value between parentheses (phone number, website, or IM address).
#: lib/extendedprofilewidget.php:169 lib/extendedprofilewidget.php:182
#: lib/extendedprofilewidget.php:206
#, php-format
msgid "(%s)"
msgstr ""

#. TRANS: Field label in experience area of extended profile.
#. TRANS: Field label in experience edit area of extended profile (which
#. company does one work for).
#: lib/extendedprofilewidget.php:326 lib/extendedprofilewidget.php:372
msgid "Company"
msgstr ""

#. TRANS: Field label in extended profile (when did one start a position or
#. education).
#: lib/extendedprofilewidget.php:332 lib/extendedprofilewidget.php:380
#: lib/extendedprofilewidget.php:426 lib/extendedprofilewidget.php:481
msgid "Start"
msgstr ""

#. TRANS: Field label in extended profile (when did one end a position or
#. education).
#: lib/extendedprofilewidget.php:340 lib/extendedprofilewidget.php:388
#: lib/extendedprofilewidget.php:434 lib/extendedprofilewidget.php:490
msgid "End"
msgstr ""

#. TRANS: Field value in experience area of extended profile (one still holds
#. a position).
#: lib/extendedprofilewidget.php:353
msgid "(Current)"
msgstr ""

#. TRANS: Checkbox label in experience edit area of extended profile (one
#. still works at a company).
#: lib/extendedprofilewidget.php:403
msgid "Current"
msgstr ""

#. TRANS: Field label in extended profile for specifying an academic degree.
#: lib/extendedprofilewidget.php:420 lib/extendedprofilewidget.php:464
msgid "Degree"
msgstr ""

#. TRANS: Field label in education area of extended profile.
#. TRANS: Field label in education edit area of extended profile.
#: lib/extendedprofilewidget.php:423 lib/extendedprofilewidget.php:472
msgid "Description"
msgstr "Mô tả"

#. TRANS: Link description in extended profile page to add another profile
#. element.
#: lib/extendedprofilewidget.php:522
msgid "Add another item"
msgstr ""

#. TRANS: Field label for undefined field in extended profile.
#: lib/extendedprofilewidget.php:633
#, php-format
msgid "TYPE: %s"
msgstr ""

#. TRANS: Button text for saving extended profile properties.
#: lib/extendedprofilewidget.php:647
msgctxt "BUTTON"
msgid "Save"
msgstr "Lưu"

#. TRANS: .
#. TRANS: Button title for saving extended profile properties.
#: lib/extendedprofilewidget.php:652
msgid "Save details"
msgstr ""


code:
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-06 16:15+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Chinese (China) (http://www.transifex.com/projects/p/gnu-social/language/zh_CN/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: zh_CN\n"
"Plural-Forms: nplurals=1; plural=0;\n"

#. TRANS: Link title for link on user profile.
#: actions/profiledetail.php:52
msgid "Edit extended profile settings"
msgstr ""

#. TRANS: Link text for link on user profile.
#: actions/profiledetail.php:54
msgid "Edit"
msgstr "编辑"

#. TRANS: Title for extended profile settings.
#: actions/profiledetailsettings.php:29
msgid "Extended profile settings"
msgstr ""

#. TRANS: Usage instructions for profile settings.
#: actions/profiledetailsettings.php:40
msgid ""
"You can update your personal profile info here so people know more about "
"you."
msgstr "在这里更新个人信息，让大家对你了解得更多。"

#. TRANS: Client error displayed when the session token does not match or is
#. not given.
#: actions/profiledetailsettings.php:63
msgid "There was a problem with your session token. Try again, please."
msgstr "你的 session 出现了一个问题，请重试。"

#. TRANS: Message given submitting a form with an unknown action.
#: actions/profiledetailsettings.php:74
msgid "Unexpected form submission."
msgstr "未预料的表单提交。"

#. TRANS: Success message after saving extended profile details.
#: actions/profiledetailsettings.php:137
msgid "Details saved."
msgstr ""

#. TRANS: Exception thrown when no date was entered in a required date field.
#. TRANS: %s is the field name.
#: actions/profiledetailsettings.php:148
#, php-format
msgid "You must supply a date for \"%s\"."
msgstr ""

#. TRANS: Exception thrown on incorrect data input.
#. TRANS: %1$s is a field name, %2$s is the incorrect input.
#: actions/profiledetailsettings.php:160
#, php-format
msgid "Invalid date entered for \"%1$s\": %2$s."
msgstr ""

#. TRANS: Exception thrown when entering an invalid URL.
#. TRANS: %s is the invalid URL.
#: actions/profiledetailsettings.php:273
#, php-format
msgid "Invalid URL: %s."
msgstr ""

#. TRANS: Server error displayed when a field could not be saved in the
#. database.
#: actions/profiledetailsettings.php:522 actions/profiledetailsettings.php:535
msgid "Could not save profile details."
msgstr ""

#. TRANS: Validation error in form for profile settings.
#. TRANS: %s is an invalid tag.
#: actions/profiledetailsettings.php:580
#, php-format
msgid "Invalid tag: \"%s\"."
msgstr "无效的标记： %s。"

#. TRANS: Server error thrown when user profile settings could not be saved.
#: actions/profiledetailsettings.php:622
msgid "Could not save profile."
msgstr "无法保存个人信息。"

#. TRANS: Server error thrown when user profile settings tags could not be
#. saved.
#: actions/profiledetailsettings.php:630
msgid "Could not save tags."
msgstr "无法保存标签。"

#. TRANS: Plugin description.
#: ExtendedProfilePlugin.php:40
msgid "UI extensions for additional profile fields."
msgstr ""

#. TRANS: Link text on user profile page leading to extended profile page.
#: ExtendedProfilePlugin.php:87
msgid "More details..."
msgstr ""

#. TRANS: Field label for extended profile properties.
#: lib/extendedprofile.php:120 lib/extendedprofile.php:131
msgid "Phone"
msgstr ""

#. TRANS: Field label for extended profile properties (Instant Messaging).
#: lib/extendedprofile.php:153 lib/extendedprofile.php:160
msgid "IM"
msgstr "即时通讯IM"

#. TRANS: Field label for extended profile properties.
#: lib/extendedprofile.php:181 lib/extendedprofile.php:188
msgid "Website"
msgstr ""

#. TRANS: Field label for extended profile properties.
#: lib/extendedprofile.php:212 lib/extendedprofile.php:224
msgid "Employer"
msgstr ""

#. TRANS: Field label for extended profile properties.
#. TRANS: Field label in education area of extended profile.
#. TRANS: Field label in education edit area of extended profile.
#: lib/extendedprofile.php:251 lib/extendedprofile.php:264
#: lib/extendedprofilewidget.php:416 lib/extendedprofilewidget.php:456
msgid "Institution"
msgstr ""

#. TRANS: Field label for extended profile properties.
#: lib/extendedprofile.php:289 lib/extendedprofile.php:338
msgid "Personal"
msgstr ""

#. TRANS: Field label for extended profile properties.
#: lib/extendedprofile.php:293
msgid "Full name"
msgstr "全名"

#. TRANS: Field label for extended profile properties.
#: lib/extendedprofile.php:299
msgid "Title"
msgstr ""

#. TRANS: Field label for extended profile properties.
#: lib/extendedprofile.php:304
msgid "Manager"
msgstr ""

#. TRANS: Field label for extended profile properties.
#: lib/extendedprofile.php:310
msgid "Location"
msgstr "位置"

#. TRANS: Field label for extended profile properties.
#: lib/extendedprofile.php:315
msgid "Bio"
msgstr "自述"

#. TRANS: Field label for extended profile properties.
#: lib/extendedprofile.php:321
msgid "Tags"
msgstr "标签"

#. TRANS: Field label for extended profile properties.
#: lib/extendedprofile.php:329
msgid "Contact"
msgstr "联系"

#. TRANS: Field label for extended profile properties.
#: lib/extendedprofile.php:342
msgid "Birthday"
msgstr ""

#. TRANS: Field label for extended profile properties.
#: lib/extendedprofile.php:348
msgid "Spouse's name"
msgstr ""

#. TRANS: Field label for extended profile properties.
#: lib/extendedprofile.php:353
msgid "Kids' names"
msgstr ""

#. TRANS: Field label for extended profile properties.
#: lib/extendedprofile.php:359
msgid "Work experience"
msgstr ""

#. TRANS: Field label for extended profile properties.
#: lib/extendedprofile.php:366
msgid "Education"
msgstr ""

#. TRANS: Title for extended profile entry deletion dialog.
#: lib/extendedprofilewidget.php:84
msgid "Confirmation Required"
msgstr ""

#. TRANS: Confirmation text for extended profile entry deletion dialog.
#: lib/extendedprofilewidget.php:87
msgid "Really delete this entry?"
msgstr ""

#. TRANS: Value between parentheses (phone number, website, or IM address).
#: lib/extendedprofilewidget.php:169 lib/extendedprofilewidget.php:182
#: lib/extendedprofilewidget.php:206
#, php-format
msgid "(%s)"
msgstr ""

#. TRANS: Field label in experience area of extended profile.
#. TRANS: Field label in experience edit area of extended profile (which
#. company does one work for).
#: lib/extendedprofilewidget.php:326 lib/extendedprofilewidget.php:372
msgid "Company"
msgstr ""

#. TRANS: Field label in extended profile (when did one start a position or
#. education).
#: lib/extendedprofilewidget.php:332 lib/extendedprofilewidget.php:380
#: lib/extendedprofilewidget.php:426 lib/extendedprofilewidget.php:481
msgid "Start"
msgstr ""

#. TRANS: Field label in extended profile (when did one end a position or
#. education).
#: lib/extendedprofilewidget.php:340 lib/extendedprofilewidget.php:388
#: lib/extendedprofilewidget.php:434 lib/extendedprofilewidget.php:490
msgid "End"
msgstr ""

#. TRANS: Field value in experience area of extended profile (one still holds
#. a position).
#: lib/extendedprofilewidget.php:353
msgid "(Current)"
msgstr ""

#. TRANS: Checkbox label in experience edit area of extended profile (one
#. still works at a company).
#: lib/extendedprofilewidget.php:403
msgid "Current"
msgstr ""

#. TRANS: Field label in extended profile for specifying an academic degree.
#: lib/extendedprofilewidget.php:420 lib/extendedprofilewidget.php:464
msgid "Degree"
msgstr ""

#. TRANS: Field label in education area of extended profile.
#. TRANS: Field label in education edit area of extended profile.
#: lib/extendedprofilewidget.php:423 lib/extendedprofilewidget.php:472
msgid "Description"
msgstr "描述"

#. TRANS: Link description in extended profile page to add another profile
#. element.
#: lib/extendedprofilewidget.php:522
msgid "Add another item"
msgstr ""

#. TRANS: Field label for undefined field in extended profile.
#: lib/extendedprofilewidget.php:633
#, php-format
msgid "TYPE: %s"
msgstr ""

#. TRANS: Button text for saving extended profile properties.
#: lib/extendedprofilewidget.php:647
msgctxt "BUTTON"
msgid "Save"
msgstr "保存"

#. TRANS: .
#. TRANS: Button title for saving extended profile properties.
#: lib/extendedprofilewidget.php:652
msgid "Save details"
msgstr ""

repo_name: piratas/tortuga

license: mit


code:
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-06 16:15+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Chinese (Taiwan) (http://www.transifex.com/projects/p/gnu-social/language/zh_TW/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: zh_TW\n"
"Plural-Forms: nplurals=1; plural=0;\n"

#. TRANS: Link title for link on user profile.
#: actions/profiledetail.php:52
msgid "Edit extended profile settings"
msgstr ""

#. TRANS: Link text for link on user profile.
#: actions/profiledetail.php:54
msgid "Edit"
msgstr ""

#. TRANS: Title for extended profile settings.
#: actions/profiledetailsettings.php:29
msgid "Extended profile settings"
msgstr ""

#. TRANS: Usage instructions for profile settings.
#: actions/profiledetailsettings.php:40
msgid ""
"You can update your personal profile info here so people know more about "
"you."
msgstr ""

#. TRANS: Client error displayed when the session token does not match or is
#. not given.
#: actions/profiledetailsettings.php:63
msgid "There was a problem with your session token. Try again, please."
msgstr ""

#. TRANS: Message given submitting a form with an unknown action.
#: actions/profiledetailsettings.php:74
msgid "Unexpected form submission."
msgstr ""

#. TRANS: Success message after saving extended profile details.
#: actions/profiledetailsettings.php:137
msgid "Details saved."
msgstr ""

#. TRANS: Exception thrown when no date was entered in a required date field.
#. TRANS: %s is the field name.
#: actions/profiledetailsettings.php:148
#, php-format
msgid "You must supply a date for \"%s\"."
msgstr ""

#. TRANS: Exception thrown on incorrect data input.
#. TRANS: %1$s is a field name, %2$s is the incorrect input.
#: actions/profiledetailsettings.php:160
#, php-format
msgid "Invalid date entered for \"%1$s\": %2$s."
msgstr ""

#. TRANS: Exception thrown when entering an invalid URL.
#. TRANS: %s is the invalid URL.
#: actions/profiledetailsettings.php:273
#, php-format
msgid "Invalid URL: %s."
msgstr ""

#. TRANS: Server error displayed when a field could not be saved in the
#. database.
#: actions/profiledetailsettings.php:522 actions/profiledetailsettings.php:535
msgid "Could not save profile details."
msgstr ""

#. TRANS: Validation error in form for profile settings.
#. TRANS: %s is an invalid tag.
#: actions/profiledetailsettings.php:580
#, php-format
msgid "Invalid tag: \"%s\"."
msgstr ""

#. TRANS: Server error thrown when user profile settings could not be saved.
#: actions/profiledetailsettings.php:622
msgid "Could not save profile."
msgstr ""

#. TRANS: Server error thrown when user profile settings tags could not be
#. saved.
#: actions/profiledetailsettings.php:630
msgid "Could not save tags."
msgstr "無法儲存個人資料"

#. TRANS: Plugin description.
#: ExtendedProfilePlugin.php:40
msgid "UI extensions for additional profile fields."
msgstr ""

#. TRANS: Link text on user profile page leading to extended profile page.
#: ExtendedProfilePlugin.php:87
msgid "More details..."
msgstr ""

#. TRANS: Field label for extended profile properties.
#: lib/extendedprofile.php:120 lib/extendedprofile.php:131
msgid "Phone"
msgstr ""

#. TRANS: Field label for extended profile properties (Instant Messaging).
#: lib/extendedprofile.php:153 lib/extendedprofile.php:160
msgid "IM"
msgstr ""

#. TRANS: Field label for extended profile properties.
#: lib/extendedprofile.php:181 lib/extendedprofile.php:188
msgid "Website"
msgstr ""

#. TRANS: Field label for extended profile properties.
#: lib/extendedprofile.php:212 lib/extendedprofile.php:224
msgid "Employer"
msgstr ""

#. TRANS: Field label for extended profile properties.
#. TRANS: Field label in education area of extended profile.
#. TRANS: Field label in education edit area of extended profile.
#: lib/extendedprofile.php:251 lib/extendedprofile.php:264
#: lib/extendedprofilewidget.php:416 lib/extendedprofilewidget.php:456
msgid "Institution"
msgstr ""

#. TRANS: Field label for extended profile properties.
#: lib/extendedprofile.php:289 lib/extendedprofile.php:338
msgid "Personal"
msgstr ""

#. TRANS: Field label for extended profile properties.
#: lib/extendedprofile.php:293
msgid "Full name"
msgstr "全名"

#. TRANS: Field label for extended profile properties.
#: lib/extendedprofile.php:299
msgid "Title"
msgstr ""

#. TRANS: Field label for extended profile properties.
#: lib/extendedprofile.php:304
msgid "Manager"
msgstr ""

#. TRANS: Field label for extended profile properties.
#: lib/extendedprofile.php:310
msgid "Location"
msgstr "地點"

#. TRANS: Field label for extended profile properties.
#: lib/extendedprofile.php:315
msgid "Bio"
msgstr "自我介紹"

#. TRANS: Field label for extended profile properties.
#: lib/extendedprofile.php:321
msgid "Tags"
msgstr ""

#. TRANS: Field label for extended profile properties.
#: lib/extendedprofile.php:329
msgid "Contact"
msgstr ""

#. TRANS: Field label for extended profile properties.
#: lib/extendedprofile.php:342
msgid "Birthday"
msgstr ""

#. TRANS: Field label for extended profile properties.
#: lib/extendedprofile.php:348
msgid "Spouse's name"
msgstr ""

#. TRANS: Field label for extended profile properties.
#: lib/extendedprofile.php:353
msgid "Kids' names"
msgstr ""

#. TRANS: Field label for extended profile properties.
#: lib/extendedprofile.php:359
msgid "Work experience"
msgstr ""

#. TRANS: Field label for extended profile properties.
#: lib/extendedprofile.php:366
msgid "Education"
msgstr ""

#. TRANS: Title for extended profile entry deletion dialog.
#: lib/extendedprofilewidget.php:84
msgid "Confirmation Required"
msgstr ""

#. TRANS: Confirmation text for extended profile entry deletion dialog.
#: lib/extendedprofilewidget.php:87
msgid "Really delete this entry?"
msgstr ""

#. TRANS: Value between parentheses (phone number, website, or IM address).
#: lib/extendedprofilewidget.php:169 lib/extendedprofilewidget.php:182
#: lib/extendedprofilewidget.php:206
#, php-format
msgid "(%s)"
msgstr ""

#. TRANS: Field label in experience area of extended profile.
#. TRANS: Field label in experience edit area of extended profile (which
#. company does one work for).
#: lib/extendedprofilewidget.php:326 lib/extendedprofilewidget.php:372
msgid "Company"
msgstr ""

#. TRANS: Field label in extended profile (when did one start a position or
#. education).
#: lib/extendedprofilewidget.php:332 lib/extendedprofilewidget.php:380
#: lib/extendedprofilewidget.php:426 lib/extendedprofilewidget.php:481
msgid "Start"
msgstr ""

#. TRANS: Field label in extended profile (when did one end a position or
#. education).
#: lib/extendedprofilewidget.php:340 lib/extendedprofilewidget.php:388
#: lib/extendedprofilewidget.php:434 lib/extendedprofilewidget.php:490
msgid "End"
msgstr ""

#. TRANS: Field value in experience area of extended profile (one still holds
#. a position).
#: lib/extendedprofilewidget.php:353
msgid "(Current)"
msgstr ""

#. TRANS: Checkbox label in experience edit area of extended profile (one
#. still works at a company).
#: lib/extendedprofilewidget.php:403
msgid "Current"
msgstr ""

#. TRANS: Field label in extended profile for specifying an academic degree.
#: lib/extendedprofilewidget.php:420 lib/extendedprofilewidget.php:464
msgid "Degree"
msgstr ""

#. TRANS: Field label in education area of extended profile.
#. TRANS: Field label in education edit area of extended profile.
#: lib/extendedprofilewidget.php:423 lib/extendedprofilewidget.php:472
msgid "Description"
msgstr ""

#. TRANS: Link description in extended profile page to add another profile
#. element.
#: lib/extendedprofilewidget.php:522
msgid "Add another item"
msgstr ""

#. TRANS: Field label for undefined field in extended profile.
#: lib/extendedprofilewidget.php:633
#, php-format
msgid "TYPE: %s"
msgstr ""

#. TRANS: Button text for saving extended profile properties.
#: lib/extendedprofilewidget.php:647
msgctxt "BUTTON"
msgid "Save"
msgstr ""

#. TRANS: .
#. TRANS: Button title for saving extended profile properties.
#: lib/extendedprofilewidget.php:652
msgid "Save details"
msgstr ""


code:
<?php
/**
 * StatusNet - the distributed open-source microblogging tool
 * Copyright (C) 2010-2011, StatusNet, Inc.
 *
 * A plugin for integrating Facebook with StatusNet. Includes single-sign-on
 * and publishing notices to Facebook using Facebook's Graph API.
 *
 * PHP version 5
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @category  Plugin
 * @package   StatusNet
 * @author    Zach Copley <zach@status.net>
 * @copyright 2011 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 * @link      http://status.net/
 */

if (!defined('STATUSNET')) {
    exit(1);
}

define("FACEBOOK_SERVICE", 2);

/**
 * Main class for Facebook Bridge plugin
 *
 * @category  Plugin
 * @package   StatusNet
 * @author    Zach Copley <zach@status.net>
 * @copyright 2010-2011 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 * @link      http://status.net/
 */
class FacebookBridgePlugin extends Plugin
{
    public $appId;  // Facebook application ID
    public $secret; // Facebook application secret

    public $facebook = null; // Facebook application instance
    public $dir      = null; // Facebook plugin dir

    /**
     * Initializer for this plugin
     *
     * Gets an instance of the Facebook API client object
     *
     * @return boolean hook value; true means continue processing, false means stop.
     */
    function initialize()
    {

        // Allow the id and key to be passed in
        // Control panel will override

        if (isset($this->appId)) {
            $appId = common_config('facebook', 'appid');
            if (empty($appId)) {
                Config::save(
                    'facebook',
                    'appid',
                    $this->appId
                );
            }
        }

        if (isset($this->secret)) {
            $secret = common_config('facebook', 'secret');
            if (empty($secret)) {
                Config::save('facebook', 'secret', $this->secret);
            }
        }

        $this->facebook = Facebookclient::getFacebook(
            $this->appId,
            $this->secret
        );

        return true;
    }

    /**
     * Load related modules when needed
     *
     * @param string $cls Name of the class to be loaded
     *
     * @return boolean hook value; true means continue processing, false means stop.
     */
    function onAutoload($cls)
    {
        $dir = dirname(__FILE__);

        switch ($cls)
        {
        case 'Facebook': // Facebook PHP SDK
            include_once $dir . '/extlib/base_facebook.php';
            include_once $dir . '/extlib/facebook.php';
            return false;
        }

        return parent::onAutoload($cls);
    }

    /**
     * Database schema setup
     *
     * We maintain a table mapping StatusNet notices to Facebook items
     *
     * @see Schema
     * @see ColumnDef
     *
     * @return boolean hook value; true means continue processing, false means stop.
     */
    function onCheckSchema()
    {
        $schema = Schema::get();
        $schema->ensureTable('notice_to_item', Notice_to_item::schemaDef());
        return true;
    }

    /*
     * Does this $action need the Facebook JavaScripts?
     */
    function needsScripts($action)
    {
        static $needy = array(
            'FacebookloginAction',
            'FacebookfinishloginAction',
            'FacebookadminpanelAction',
            'FacebooksettingsAction'
        );

        if (in_array(get_class($action), $needy)) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * Map URLs to actions
     *
     * @param URLMapper $m path-to-action mapper
     *
     * @return boolean hook value; true means continue processing, false means stop.
     */
    public function onRouterInitialized(URLMapper $m)
    {
        // Always add the admin panel route
        $m->connect('panel/facebook', array('action' => 'facebookadminpanel'));

        $m->connect(
            'main/facebooklogin',
            array('action' => 'facebooklogin')
        );
        $m->connect(
            'main/facebookfinishlogin',
            array('action' => 'facebookfinishlogin')
        );
        $m->connect(
            'settings/facebook',
            array('action' => 'facebooksettings')
        );
        $m->connect(
            'facebook/deauthorize',
            array('action' => 'facebookdeauthorize')
        );

        return true;
    }

    /*
     * Add a login tab for Facebook, but only if there's a Facebook
     * application defined for the plugin to use.
     *
     * @param Action $action the current action
     *
     * @return void
     */
    function onEndLoginGroupNav($action)
    {
        $action_name = $action->trimmed('action');

        if ($this->hasApplication()) {

            $action->menuItem(
                // TRANS: Menu item for "Facebook" login.
                common_local_url('facebooklogin'),
                _m('MENU', 'Facebook'),
                // TRANS: Menu title for "Facebook" login.
                _m('Login or register using Facebook.'),
               'facebooklogin' === $action_name
            );
        }

        return true;
    }

    /**
     * If the plugin's installed, this should be accessible to admins
     */
    function onAdminPanelCheck($name, &$isOK)
    {
        if ($name == 'facebook') {
            $isOK = true;
            return false;
        }

        return true;
    }

    /**
     * Add a Facebook tab to the admin panels
     *
     * @param Widget $nav Admin panel nav
     *
     * @return boolean hook value
     */
    function onEndAdminPanelNav($nav)
    {
        if (AdminPanelAction::canAdmin('facebook')) {

            $action_name = $nav->action->trimmed('action');

            $nav->out->menuItem(
                common_local_url('facebookadminpanel'),
                // TRANS: Menu item for "Facebook" in administration panel.
                _m('MENU','Facebook'),
                // TRANS: Menu title for "Facebook" in administration panel.
                _m('Facebook integration configuration.'),
                $action_name == 'facebookadminpanel',
                'nav_facebook_admin_panel'
            );
        }

        return true;
    }

    /*
     * Add a tab for user-level Facebook settings if the user
     * has a link to Facebook
     *
     * @param Action $action the current action
     *
     * @return void
     */
    function onEndConnectSettingsNav($action)
    {
        if ($this->hasApplication()) {
            $action_name = $action->trimmed('action');

            $user = common_current_user();

            $flink = null;

            if (!empty($user)) {
                $flink = Foreign_link::getByUserID(
                    $user->id,
                    FACEBOOK_SERVICE
                );
            }

            if (!empty($flink)) {

                $action->menuItem(
                    common_local_url('facebooksettings'),
                    // TRANS: Menu item for "Facebook" in user settings.
                    _m('MENU','Facebook'),
                    // TRANS: Menu title for "Facebook" in user settings.
                    _m('Facebook settings.'),
                    $action_name === 'facebooksettings'
                );
            }
        }
    }

    /*
     * Is there a Facebook application for the plugin to use?
     *
     * Checks to see if a Facebook application ID and secret
     * have been configured and a valid Facebook API client
     * object exists.
     *
     */
    function hasApplication()
    {
        if (!empty($this->facebook)) {

            $appId  = $this->facebook->getAppId();
            $secret = $this->facebook->getApiSecret();

            if (!empty($appId) && !empty($secret)) {
                return true;
            }
        }

        return false;
    }

    /*
     * Output a Facebook div for the Facebook JavaSsript SDK to use
     *
     * @param Action $action the current action
     *
     */
    function onStartShowHeader($action)
    {
        // output

 as close to  as possible
        $action->element('div', array('id' => 'fb-root'));
        return true;
    }

    /*
     * Load the Facebook JavaScript SDK on pages that need them.
     *
     * @param Action $action the current action
     *
     */
    function onEndShowScripts($action)
    {
        if ($this->needsScripts($action)) {

            $action->script('https://connect.facebook.net/en_US/all.js');

            $script = <<<ENDOFSCRIPT
function setCookie(name, value) {
    var date = new Date();
    date.setTime(date.getTime() + (5 * 60 * 1000)); // 5 mins
    var expires = "; expires=" + date.toGMTString();
    document.cookie = name + "=" + value + expires + "; path=/";
}

FB.init({appId: %1\$s, status: true, cookie: true, xfbml: true, oauth: true});

$('#facebook_button').bind('click', function(event) {

    event.preventDefault();

    FB.login(function(response) {
        if (response.authResponse) {
            // put the access token in a cookie for the next step
            setCookie('fb_access_token', response.authResponse.accessToken);
            window.location.href = '%2\$s';
        } else {
            // NOP (user cancelled login)
        }
    }, {scope:'read_stream,publish_stream,offline_access,user_status,user_location,user_website,email'});
});
ENDOFSCRIPT;

            $action->inlineScript(
                sprintf(
                    $script,
                    json_encode($this->facebook->getAppId()),
                    common_local_url('facebookfinishlogin')
                )
            );
        }
    }

    /*
     * Log the user out of Facebook, per the Facebook authentication guide
     *
     * @param Action action the current action
     */
    function onStartLogout($action)
    {
        if ($this->hasApplication()) {

            $cur = common_current_user();
            $flink = Foreign_link::getByUserID($cur->id, FACEBOOK_SERVICE);

            if (!empty($flink)) {

                $this->facebook->setAccessToken($flink->credentials);

                if (common_config('singleuser', 'enabled')) {
                    $user = User::singleUser();

                    $destination = common_local_url(
                        'showstream',
                        array('nickname' => $user->nickname)
                    );
                } else {
                    $destination = common_local_url('public');
                }

                $logoutUrl = $this->facebook->getLogoutUrl(
                    array('next' => $destination)
                );

                common_log(
                    LOG_INFO,
                    sprintf(
                        "Logging user out of Facebook (fbuid = %s)",
                        $fbuid
                    ),
                    __FILE__
                );

                $action->logout();

                common_redirect($logoutUrl, 303);
            }

            return true;
        }
    }

    /*
     * Add fbml namespace to our HTML, so Facebook's JavaScript SDK can parse
     * and render XFBML tags
     *
     * @param Action    $action   the current action
     * @param array     $attrs    array of attributes for the HTML tag
     *
     * @return nothing
     */
    function onStartHtmlElement($action, $attrs) {

        if ($this->needsScripts($action)) {
            $attrs = array_merge(
                $attrs,
                array('xmlns:fb' => 'http://www.facebook.com/2008/fbml')
            );
        }

        return true;
    }

    /**
     * Add a Facebook queue item for each notice
     *
     * @param Notice $notice      the notice
     * @param array  &$transports the list of transports (queues)
     *
     * @return boolean hook return
     */
    function onStartEnqueueNotice($notice, &$transports)
    {
        if (self::hasApplication() && $notice->isLocal() && $notice->inScope(null)) {
            array_push($transports, 'facebook');
        }
        return true;
    }

    /**
     * Register Facebook notice queue handler
     *
     * @param QueueManager $manager
     *
     * @return boolean hook return
     */
    function onEndInitializeQueueManager($manager)
    {
        if (self::hasApplication()) {
            $manager->connect('facebook', 'FacebookQueueHandler');
        }
        return true;
    }

    /*
     * Use SSL for Facebook stuff
     *
     * @param string $action name
     * @param boolean $ssl outval to force SSL
     * @return mixed hook return value
     */
    function onSensitiveAction($action, &$ssl)
    {
        $sensitive = array(
            'facebookadminpanel',
            'facebooksettings',
            'facebooklogin',
            'facebookfinishlogin'
        );

        if (in_array($action, $sensitive)) {
            $ssl = true;
            return false;
        } else {
            return true;
        }
    }

    /**
     * If a notice gets deleted, remove the Notice_to_item mapping and
     * delete the item on Facebook
     *
     * @param User   $user   The user doing the deleting
     * @param Notice $notice The notice getting deleted
     *
     * @return boolean hook value
     */
    function onStartDeleteOwnNotice(User $user, Notice $notice)
    {
        $client = new Facebookclient($notice);
        $client->streamRemove();

        return true;
    }

    /**
     * Notify remote users when their notices get favorited.
     *
     * @param Profile or User $profile of local user doing the faving
     * @param Notice $notice being favored
     * @return hook return value
     */
    function onEndFavorNotice(Profile $profile, Notice $notice)
    {
        $client = new Facebookclient($notice, $profile);
        $client->like();

        return true;
    }

    /**
     * Notify remote users when their notices get de-favorited.
     *
     * @param Profile $profile Profile person doing the de-faving
     * @param Notice  $notice  Notice being favored
     *
     * @return hook return value
     */
    function onEndDisfavorNotice(Profile $profile, Notice $notice)
    {
        $client = new Facebookclient($notice, $profile);
        $client->unLike();

        return true;
    }

    /**
     * Add links in the user's profile block to their Facebook profile URL.
     *
     * @param Profile $profile The profile being shown
     * @param Array   &$links  Writeable array of arrays (href, text, image).
     *
     * @return boolean hook value (true)
     */

    function onOtherAccountProfiles($profile, &$links)
    {
        $fuser = null;

        $flink = Foreign_link::getByUserID($profile->id, FACEBOOK_SERVICE);

        if (!empty($flink)) {

            $fuser = $this->getFacebookUser($flink->foreign_id);

            if (!empty($fuser)) {
                $links[] = array("href" => $fuser->link,
                                 "text" => sprintf(_("%s on Facebook"), $fuser->name),
                                 "image" => $this->path("images/f_logo.png"));
            }
        }

        return true;
    }

    function getFacebookUser($id) {

        $key = Cache::key(sprintf("FacebookBridgePlugin:userdata:%s", $id));

        $c = Cache::instance();

        if ($c) {
            $obj = $c->get($key);
            if ($obj) {
                return $obj;
            }
        }

        $url = sprintf("https://graph.facebook.com/%s", $id);
        $client = new HTTPClient();
        $resp = $client->get($url);

        if (!$resp->isOK()) {
            return null;
        }

        $user = json_decode($resp->getBody());

        if ($user->error) {
            return null;
        }

        if ($c) {
            $c->set($key, $user);
        }

        return $user;
    }

    /*
     * Add version info for this plugin
     *
     * @param array &$versions    plugin version descriptions
     */
    function onPluginVersion(&$versions)
    {
        $versions[] = array(
            'name' => 'Facebook Bridge',
            'version' => GNUSOCIAL_VERSION,
            'author' => 'Craig Andrews, Zach Copley',
            'homepage' => 'http://status.net/wiki/Plugin:FacebookBridge',
            'rawdescription' =>
             // TRANS: Plugin description.
            _m('A plugin for integrating StatusNet with Facebook.')
        );

        return true;
    }
}


code:
Facebook Bridge Plugin

The Facebook Bridge plugin allows you to integrate your StatusNet site
with Facebook. It uses Facebook's new SDKs, the Graph API and OAuth
2.0, and supercedes the previous "Facebook" plugin, which relied on the
Facebook's now deprecated "Old REST API". The other major difference is
the troublesome and confusing Facebook Canvas Application has been
removed.

Note: Do NOT try to run the old Facebook plugin and this plugin at the same
time. It won't work.

Features for the new Facebook Bridge Plugin:

- "Login with Facebook" (AKA single-sign-on using Facebook Connect for
  authentication).

- Registration with Facebook Connect, including automatic profile creation
  based on users' Facebook accounts.

- Post mirroring -- posting a notice on StatusNet automatically creates a post
  on Facebook, deleting it on StatusNet deletes it on Facebook, and faving it
  "likes" it on Facebook.

Upgrading from the old Facebook plugin
======================================

Remove the addPlugin('Facebook') statement for the old Facebook plugin
from your config.php and adjust your existing Facebook application using
the setup instructions below as a guide, then enable the new plugin (also
described below). Existing users who have setup post mirroring shouldn't
have to do anything. The new Facebook Bridge plugin will keep their notices
flowing to Facebook.

Setup
=====

There are two parts configuring the Facebook Bridge plugin -- setup on the
Facebook side, and setup on the StatusNet side.

Setup (Facebook side)
=====================

The first step is to login to Facebook and register a Facebook application
to get an application ID and secret.

Use the handy Facebook application setup wizard:

http://developers.facebook.com/setup/

Once you've set up your application, you'll need to enter the Facebook Developer
dashboard (http://www.facebook.com/developers/editapp.php?app_id=YOUR_APPLICATION_ID)
and customize a few things:

About tab
---------

Set your logos, application description, etc. as you see fit.

Web Site tab
------------

Make a note of the "Application ID" and "Application Secret" Facebook generated
for your application. You'll need both of those later.

Make sure "Site URL" points to your StatusNet installation
(e.g.: http://example.net/).

- Special Note for multi-site Status Networks: enter your domain (SLD and TLD)
  in the "Site Domain" field. (e.g.: status.net). This will allow a single
  Facebook Application to work with all your network subdomains (*.status.net).

Facebook Integration tab
------------------------

Use the default settings.

Mobile and Devices tab
----------------------

Use the default settings.

Advanced tab
------------

In the Authentication section, set the "Deauthorize Callback" to
http://YOURSITE/facebook/deauthorize (e.g.: http://example.net/facebook/deauthorize).

In the Migrations section, ensure that "OAuth 2.0 for Canvas" is set to
"Enabled". It probably already will be, but double check.

The default settings should suffice for everything else.

Setup (StatusNet side)
======================

To enable the Facebook Bridge plugin, add

    addPlugin('FacebookBridge');

to you config.php.

Next login to your StatusNet site as a user with the administrator role, and
navigate to the admin menu. You should see a new tab for the Facebook admin
panel, titled "Facebook".  Enter your Facebook application ID and secret in
that admin panel and hit save. After that, the Facebook Bridge Plugin should
be ready to use.

path: plugins/FacebookBridge/README


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Facebook integration administration panel
 *
 * PHP version 5
 *
 * LICENCE: This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @category  Settings
 * @package   StatusNet
 * @author    Zach Copley <zach@status.net>
 * @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);
}

/**
 * Administer global Facebook integration settings
 *
 * @category Admin
 * @package  StatusNet
 * @author   Zach Copley <zach@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link     http://status.net/
 */
class FacebookadminpanelAction extends AdminPanelAction
{
    /**
     * Returns the page title
     *
     * @return string page title
     */
    function title()
    {
        // TRANS: Title for Facebook administration panel.
        return _m('TITLE','Facebook integration settings');
    }

    /**
     * Instructions for using this form.
     *
     * @return string instructions
     */
    function getInstructions()
    {
        // TRANS: Instruction for Facebook administration panel.
        return _m('Facebook integration settings');
    }

    /**
     * Show the Facebook admin panel form
     *
     * @return void
     */
    function showForm()
    {
        $form = new FacebookAdminPanelForm($this);
        $form->show();
        return;
    }

    /**
     * Save settings from the form
     *
     * @return void
     */
    function saveSettings()
    {
        static $settings = array(
            'facebook' => array('appid', 'secret'),
        );

        $values = array();

        foreach ($settings as $section => $parts) {
            foreach ($parts as $setting) {
                $values[$section][$setting]
                    = $this->trimmed($setting);
            }
        }

        // This throws an exception on validation errors
        $this->validate($values);

        // assert(all values are valid);

        $config = new Config();

        $config->query('BEGIN');

        foreach ($settings as $section => $parts) {
            foreach ($parts as $setting) {
                Config::save($section, $setting, $values[$section][$setting]);
            }
        }

        $config->query('COMMIT');

        return;
    }

    function validate(&$values)
    {
        // appId, key and secret (can't be too long)

        if (mb_strlen($values['facebook']['appid']) > 255) {
            $this->clientError(
                // TRANS: Client error displayed when providing too long a Facebook application ID.
                _m("Invalid Facebook ID. Maximum length is 255 characters.")
            );
        }

        if (mb_strlen($values['facebook']['secret']) > 255) {
            $this->clientError(
                // TRANS: Client error displayed when providing too long a Facebook secret key.
                _m("Invalid Facebook secret. Maximum length is 255 characters.")
            );
        }
    }
}

class FacebookAdminPanelForm extends AdminForm
{
    /**
     * ID of the form
     *
     * @return int ID of the form
     */
    function id()
    {
        return 'facebookadminpanel';
    }

    /**
     * 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('facebookadminpanel');
    }

    /**
     * Data elements of the form
     *
     * @return void
     */
    function formData()
    {
        $this->out->elementStart(
            'fieldset',
            array('id' => 'settings_facebook-application')
        );
        // TRANS: Fieldset legend.
        $this->out->element('legend', null, _m('Facebook application settings'));
        $this->out->elementStart('ul', 'form_data');

        $this->li();
        $this->input(
            'appid',
            // TRANS: Field label for Facebook application ID.
            _m('Application ID'),
            // TRANS: Field title for Facebook application ID.
            _m('ID of your Facebook application.'),
            'facebook'
        );
        $this->unli();

        $this->li();
        $this->input(
            'secret',
            // TRANS: Field label for Facebook secret key.
            _m('Secret'),
            // TRANS: Field title for Facebook secret key.
            _m('Application secret.'),
            'facebook'
        );
        $this->unli();

        $this->out->elementEnd('ul');
        $this->out->elementEnd('fieldset');
    }

    /**
     * Action elements
     *
     * @return void
     */
    function formActions()
    {
        // TRANS: Button text to save Facebook integration settings.
        $this->out->submit('submit', _m('BUTTON','Save'),
                           // TRANS: Button title to save Facebook integration settings.
                           'submit', null, _m('Save Facebook settings.'));
    }
}


code:
<?php
/**
 * StatusNet - the distributed open-source microblogging tool
 * Copyright (C) 2010-2011, StatusNet, Inc.
 *
 * An action that handles deauthorize callbacks from Facebook
 *
 * PHP version 5
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @category  Plugin
 * @package   StatusNet
 * @author    Zach Copley <zach@status.net>
 * @copyright 2010-2011 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 * @link      http://status.net/
 */

if (!defined('STATUSNET')) {
    exit(1);
}

/*
 * Action class for handling deauthorize callbacks from Facebook. If the user
 * doesn't have a password let her know she'll need to contact the site
 * admin to get back into her account (if possible).
 */
class FacebookdeauthorizeAction extends Action
{
    private $facebook;

    /**
     * For initializing members of the class.
     *
     * @param array $args misc. arguments
     *
     * @return boolean true
     */
    function prepare($args)
    {
        $this->facebook = Facebookclient::getFacebook();

        return true;
    }

    /**
     * Handler method
     *
     * @param array $args is ignored since it's now passed in in prepare()
     */
    function handle($args)
    {
        parent::handle($args);

        $data = $this->facebook->getSignedRequest();

        if (isset($data['user_id'])) {

            $fbuid = $data['user_id'];

            $flink = Foreign_link::getByForeignID($fbuid, FACEBOOK_SERVICE);
            $user = $flink->getUser();

            // Remove the link to Facebook
            $result = $flink->delete();

            if (!$result) {
                common_log_db_error($flink, 'DELETE', __FILE__);
                common_log(
                    LOG_WARNING,
                    sprintf(
                        'Unable to delete Facebook foreign link '
                            . 'for %s (%d), fbuid %d',
                        $user->nickname,
                        $user->id,
                        $fbuid
                    ),
                    __FILE__
                );
                return;
            }

            common_log(
                LOG_INFO,
                sprintf(
                    'Facebook callback: %s (%d), fbuid %d has deauthorized '
                        . 'the Facebook application.',
                    $user->nickname,
                    $user->id,
                    $fbuid
                ),
                __FILE__
            );

            // Warn the user about being locked out of their account
            // if we can.
            if (empty($user->password) && !empty($user->email)) {
                Facebookclient::emailWarn($user);
            } else {
                common_log(
                    LOG_WARNING,
                    sprintf(
                        '%s (%d), fbuid %d has deauthorized his/her Facebook '
                        . 'connection but hasn\'t set a password so s/he '
                        . 'is locked out.',
                        $user->nickname,
                        $user->id,
                        $fbuid
                    ),
                    __FILE__
                );
            }

        } else {
            if (!empty($data)) {
                common_log(
                    LOG_WARNING,
                    sprintf(
                        'Facebook called the deauthorize callback '
                        . ' but didn\'t provide a user ID.'
                    ),
                    __FILE__
                );
            } else {
                // It probably wasn't Facebook that hit this action,
                // so redirect to the public timeline
                common_redirect(common_local_url('public'), 303);
            }
        }
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Login or register a local user based on a Facebook user
 *
 * PHP version 5
 *
 * LICENCE: This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @category  Plugin
 * @package   StatusNet
 * @author    Zach Copley <zach@status.net>
 * @copyright 2010-2011 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 FacebookfinishloginAction extends Action
{
    private $fbuid       = null; // Facebook user ID
    private $fbuser      = null; // Facebook user object (JSON)
    private $accessToken = null; // Access token provided by Facebook JS API

    function prepare($args) {
        parent::prepare($args);

        // Check cookie for a valid access_token

        if (isset($_COOKIE['fb_access_token'])) {
            $this->accessToken = $_COOKIE['fb_access_token'];
        }

        if (empty($this->accessToken)) {
            $this->clientError(_m("Unable to authenticate you with Facebook."));
        }

        $graphUrl = 'https://graph.facebook.com/me?access_token=' . urlencode($this->accessToken);
        $this->fbuser = json_decode(file_get_contents($graphUrl));

        if (empty($this->fbuser)) {
            // log badness

            list($proxy, $ip) = common_client_ip();

            common_log(
                LOG_WARNING,
                    sprintf(
                        'Failed Facebook authentication attempt, proxy = %s, ip = %s.',
                         $proxy,
                         $ip
                    ),
                    __FILE__
            );

            $this->clientError(
                // TRANS: Client error displayed when trying to connect to Facebook while not logged in.
                _m('You must be logged into Facebook to register a local account using Facebook.')
            );
        }

        $this->fbuid  = $this->fbuser->id;
        // OKAY, all is well... proceed to register
        return true;
    }

    function handle($args)
    {
        parent::handle($args);

        if (common_is_real_login()) {

            // This will throw a client exception if the user already
            // has some sort of foreign_link to Facebook.

            $this->checkForExistingLink();

            // Possibly reconnect an existing account

            $this->connectUser();

        } else if ($_SERVER['REQUEST_METHOD'] == 'POST') {
            $this->handlePost();
        } else {
            $this->tryLogin();
        }
    }

    function checkForExistingLink() {

        // User is already logged in, are her accounts already linked?

        $flink = Foreign_link::getByForeignID($this->fbuid, FACEBOOK_SERVICE);

        if (!empty($flink)) {

            // User already has a linked Facebook account and shouldn't be here!

            $this->clientError(
                // TRANS: Client error displayed when trying to connect to a Facebook account that is already linked
                // TRANS: in the same StatusNet site.
                _m('There is already a local account linked with that Facebook account.')
            );
       }

       $cur = common_current_user();
       $flink = Foreign_link::getByUserID($cur->id, FACEBOOK_SERVICE);

       if (!empty($flink)) {

            // There's already a local user linked to this Facebook account.

            $this->clientError(
                // TRANS: Client error displayed when trying to connect to a Facebook account that is already linked
                // TRANS: in the same StatusNet site.
                _m('There is already a local account linked with that Facebook account.')
            );
        }
    }

    function handlePost()
    {
        $token = $this->trimmed('token');

        // CSRF protection
        if (!$token || $token != common_session_token()) {
            $this->showForm(
                // TRANS: Client error displayed when the session token does not match or is not given.
                _m('There was a problem with your session token. Try again, please.')
            );
            return;
        }

        if ($this->arg('create')) {

            if (!$this->boolean('license')) {
                $this->showForm(
                    // TRANS: Form validation error displayed when user has not agreed to the license.
                    _m('You cannot register if you do not agree to the license.'),
                    $this->trimmed('newname')
                );
                return;
            }

            // We has a valid Facebook session and the Facebook user has
            // agreed to the SN license, so create a new user
            $this->createNewUser();

        } else if ($this->arg('connect')) {

            $this->connectNewUser();

        } else {

            $this->showForm(
                // TRANS: Form validation error displayed when an unhandled error occurs.
                _m('An unknown error has occured.'),
                $this->trimmed('newname')
            );
        }
    }

    function showPageNotice()
    {
        if ($this->error) {

            $this->element('div', array('class' => 'error'), $this->error);

        } else {

            $this->element(
                'div', 'instructions',
                sprintf(
                    // TRANS: Form instructions for connecting to Facebook.
                    // TRANS: %s is the site name.
                    _m('This is the first time you have logged into %s so we must connect your Facebook to a local account. You can either create a new local account, or connect with an existing local account.'),
                    common_config('site', 'name')
                )
            );
        }
    }

    function title()
    {
        // TRANS: Page title.
        return _m('Facebook Setup');
    }

    function showForm($error=null, $username=null)
    {
        $this->error = $error;
        $this->username = $username;

        $this->showPage();
    }

    function showPage()
    {
        parent::showPage();
    }

    /**
     * @todo FIXME: Much of this duplicates core code, which is very fragile.
     * Should probably be replaced with an extensible mini version of
     * the core registration form.
     */
    function showContent()
    {
        if (!empty($this->message_text)) {
            $this->element('p', null, $this->message);
            return;
        }

        $this->elementStart('form', array('method' => 'post',
                                          'id' => 'form_settings_facebook_connect',
                                          'class' => 'form_settings',
                                          'action' => common_local_url('facebookfinishlogin')));
        $this->elementStart('fieldset', array('id' => 'settings_facebook_connect_options'));
        // TRANS: Fieldset legend.
        $this->element('legend', null, _m('Connection options'));
        $this->elementStart('ul', 'form_data');
        $this->elementStart('li');
        $this->element('input', array('type' => 'checkbox',
                                      'id' => 'license',
                                      'class' => 'checkbox',
                                      'name' => 'license',
                                      'value' => 'true'));
        $this->elementStart('label', array('class' => 'checkbox', 'for' => 'license'));
        // TRANS: %s is the name of the license used by the user for their status updates.
        $message = _m('My text and files are available under %s ' .
                     'except this private data: password, ' .
                     'email address, IM address, and phone number.');
        $link = '' .
                htmlspecialchars(common_config('license', 'title')) .
                '';
        $this->raw(sprintf(htmlspecialchars($message), $link));
        $this->elementEnd('label');
        $this->elementEnd('li');
        $this->elementEnd('ul');

        $this->elementStart('fieldset');
        $this->hidden('token', common_session_token());
        $this->element('legend', null,
                       // TRANS: Fieldset legend.
                       _m('Create new account'));
        $this->element('p', null,
                       // TRANS: Form instructions.
                       _m('Create a new user with this nickname.'));
        $this->elementStart('ul', 'form_data');

        // Hook point for captcha etc
        Event::handle('StartRegistrationFormData', array($this));

        $this->elementStart('li');
        // TRANS: Field label.
        $this->input('newname', _m('New nickname'),
                     ($this->username) ? $this->username : '',
                     // TRANS: Field title.
                     _m('1-64 lowercase letters or numbers, no punctuation or spaces.'));
        $this->elementEnd('li');

        // Hook point for captcha etc
        Event::handle('EndRegistrationFormData', array($this));

        $this->elementEnd('ul');
        // TRANS: Submit button to create a new account.
        $this->submit('create', _m('BUTTON','Create'));
        $this->elementEnd('fieldset');

        $this->elementStart('fieldset');
        $this->element('legend', null,
                       // TRANS: Fieldset legend.
                       _m('Connect existing account'));
        $this->element('p', null,
                       // TRANS: Form instructions.
                       _m('If you already have an account, login with your username and password to connect it to your Facebook.'));
        $this->elementStart('ul', 'form_data');
        $this->elementStart('li');
        // TRANS: Field label.
        $this->input('nickname', _m('Existing nickname'));
        $this->elementEnd('li');
        $this->elementStart('li');
        // TRANS: Field label.
        $this->password('password', _m('Password'));
        $this->elementEnd('li');
        $this->elementEnd('ul');
        // TRANS: Submit button to connect a Facebook account to an existing StatusNet account.
        $this->submit('connect', _m('BUTTON','Connect'));
        $this->elementEnd('fieldset');

        $this->elementEnd('fieldset');
        $this->elementEnd('form');
    }

    function message($msg)
    {
        $this->message_text = $msg;
        $this->showPage();
    }

    function createNewUser()
    {
        if (!Event::handle('StartRegistrationTry', array($this))) {
            return;
        }

        if (common_config('site', 'closed')) {
            // TRANS: Client error trying to register with registrations not allowed.
            $this->clientError(_m('Registration not allowed.'));
        }

        $invite = null;

        if (common_config('site', 'inviteonly')) {
            $code = $_SESSION['invitecode'];
            if (empty($code)) {
                // TRANS: Client error trying to register with registrations 'invite only'.
                $this->clientError(_m('Registration not allowed.'));
            }

            $invite = Invitation::getKV($code);

            if (empty($invite)) {
                // TRANS: Client error trying to register with an invalid invitation code.
                $this->clientError(_m('Not a valid invitation code.'));
            }
        }

        try {
            $nickname = Nickname::normalize($this->trimmed('newname'), true);
        } catch (NicknameException $e) {
            $this->showForm($e->getMessage());
            return;
        }

        $args = array(
            'nickname' => $nickname,
            'fullname' => $this->fbuser->name,
            'homepage' => $this->fbuser->website,
            'location' => $this->fbuser->location->name
        );

        // It's possible that the email address is already in our
        // DB. It's a unique key, so we need to check
        if ($this->isNewEmail($this->fbuser->email)) {
            $args['email']           = $this->fbuser->email;
            if (isset($this->fuser->verified) && $this->fuser->verified == true) {
                $args['email_confirmed'] = true;
            }
        }

        if (!empty($invite)) {
            $args['code'] = $invite->code;
        }

        $user   = User::register($args);
        $result = $this->flinkUser($user->id, $this->fbuid);

        if (!$result) {
            // TRANS: Server error displayed when connecting to Facebook fails.
            $this->serverError(_m('Error connecting user to Facebook.'));
        }

        // Add a Foreign_user record
        Facebookclient::addFacebookUser($this->fbuser);

        $this->setAvatar($user);

        common_set_user($user);
        common_real_login(true);

        common_log(
            LOG_INFO,
            sprintf(
                'Registered new user %s (%d) from Facebook user %s, (fbuid %d)',
                $user->nickname,
                $user->id,
                $this->fbuser->name,
                $this->fbuid
            ),
            __FILE__
        );

        Event::handle('EndRegistrationTry', array($this));

        $this->goHome($user->nickname);
    }

    /*
     * Attempt to download the user's Facebook picture and create a
     * StatusNet avatar for the new user.
     */
    function setAvatar($user)
    {
         try {
            $picUrl = sprintf(
                'http://graph.facebook.com/%d/picture?type=large',
                $this->fbuser->id
            );

            // fetch the picture from Facebook
            $client = new HTTPClient();

            // fetch the actual picture
            $response = $client->get($picUrl);

            if ($response->isOk()) {

                // seems to always be jpeg, but not sure
                $tmpname = "facebook-avatar-tmp-" . common_random_hexstr(4);

                $ok = file_put_contents(
                    Avatar::path($tmpname),
                    $response->getBody()
                );

                if (!$ok) {
                    common_log(LOG_WARNING, 'Couldn\'t save tmp Facebook avatar: ' . $tmpname, __FILE__);
                } else {
                    // save it as an avatar

                    $imagefile = new ImageFile(null, Avatar::path($tmpname));
                    $filename = Avatar::filename($user->id, image_type_to_extension($imagefile->preferredType()),
                                                 180, common_timestamp());
                    // Previous docs said 180 is the "biggest img we get from Facebook"
                    $imagefile->resizeTo(Avatar::path($filename, array('width'=>180, 'height'=>180)));

                    // No need to keep the temporary file around...
                    @unlink(Avatar::path($tmpname));

                    $profile   = $user->getProfile();

                    if ($profile->setOriginal($filename)) {
                        common_log(
                            LOG_INFO,
                            sprintf(
                                'Saved avatar for %s (%d) from Facebook picture for '
                                    . '%s (fbuid %d), filename = %s',
                                 $user->nickname,
                                 $user->id,
                                 $this->fbuser->name,
                                 $this->fbuid,
                                 $filename
                             ),
                             __FILE__
                        );

                        // clean up tmp file
                    }

                }
            }
        } catch (Exception $e) {
            common_log(LOG_WARNING, 'Couldn\'t save Facebook avatar: ' . $e->getMessage(), __FILE__);
            // error isn't fatal, continue
        }
    }

    function connectNewUser()
    {
        $nickname = $this->trimmed('nickname');
        $password = $this->trimmed('password');

        if (!common_check_user($nickname, $password)) {
            // TRANS: Form validation error displayed when username/password combination is incorrect.
            $this->showForm(_m('Invalid username or password.'));
            return;
        }

        $user = User::getKV('nickname', $nickname);

        $this->tryLinkUser($user);

        common_set_user($user);
        common_real_login(true);

        // clear out the stupid cookie
        setcookie('fb_access_token', '', time() - 3600); // one hour ago

        $this->goHome($user->nickname);
    }

    function connectUser()
    {
        $user = common_current_user();
        $this->tryLinkUser($user);

        // clear out the stupid cookie
        setcookie('fb_access_token', '', time() - 3600); // one hour ago
        common_redirect(common_local_url('facebookfinishlogin'), 303);
    }

    function tryLinkUser($user)
    {
        $result = $this->flinkUser($user->id, $this->fbuid);

        if (empty($result)) {
            // TRANS: Server error displayed when connecting to Facebook fails.
            $this->serverError(_m('Error connecting user to Facebook.'));
        }
    }

    function tryLogin()
    {
        $flink = Foreign_link::getByForeignID($this->fbuid, FACEBOOK_SERVICE);

        if (!empty($flink)) {
            $user = $flink->getUser();

            if (!empty($user)) {

                common_log(
                    LOG_INFO,
                    sprintf(
                        'Logged in Facebook user %s as user %d (%s)',
                        $this->fbuid,
                        $user->nickname,
                        $user->id
                    ),
                    __FILE__
                );

                common_set_user($user);
                common_real_login(true);

                // clear out the stupid cookie
                setcookie('fb_access_token', '', time() - 3600); // one hour ago

                $this->goHome($user->nickname);
            }

        } else {
            $this->showForm(null, $this->bestNewNickname());
        }
    }

    function goHome($nickname)
    {
        $url = common_get_returnto();
        if ($url) {
            // We don't have to return to it again
            common_set_returnto(null);
        } else {
            $url = common_local_url('all',
                                    array('nickname' =>
                                          $nickname));
        }

        common_redirect($url, 303);
    }

    function flinkUser($user_id, $fbuid)
    {
        $flink = new Foreign_link();

        $flink->user_id     = $user_id;
        $flink->foreign_id  = $fbuid;
        $flink->service     = FACEBOOK_SERVICE;
        $flink->credentials = $this->accessToken;
        $flink->created     = common_sql_now();

        $flink_id = $flink->insert();

        return $flink_id;
    }

    function bestNewNickname()
    {
        try {
            $nickname = Nickname::normalize($this->fbuser->username, true);
            return $nickname;
        } catch (NicknameException $e) {
            // Failed to normalize nickname, but let's try the full name
        }

        try {
            $nickname = Nickname::normalize($this->fbuser->name, true);
            return $nickname;
        } catch (NicknameException $e) {
            // Any more ideas? Nope.
        }

        return null;
    }

    /*
     * Do we already have a user record with this email?
     * (emails have to be unique but they can change)
     *
     * @param string $email the email address to check
     *
     * @return boolean result
     */
     function isNewEmail($email)
     {
         // we shouldn't have to validate the format
         $result = User::getKV('email', $email);

         if (empty($result)) {
             return true;
         }

         return false;
     }
}


code:
<?php
/**
 * StatusNet - the distributed open-source microblogging tool
 * Copyright (C) 2010, StatusNet, Inc.
 *
 * An action for logging in with Facebook
 *
 * PHP version 5
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @category  Plugin
 * @package   StatusNet
 * @author    Zach Copley <zach@status.net>
 * @copyright 2010-2011 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 * @link      http://status.net/
 */

if (!defined('STATUSNET')) {
    exit(1);
}

class FacebookloginAction extends Action
{
    function handle($args)
    {
        parent::handle($args);

        if (common_is_real_login()) {
            // TRANS: Client error displayed when trying to login while already logged in.
            $this->clientError(_m('Already logged in.'));
        } else {
            $this->showPage();
        }
    }

    function getInstructions()
    {
        // TRANS: Form instructions.
        return _m('Login with your Facebook Account');
    }

    function showPageNotice()
    {
        $instr = $this->getInstructions();
        $output = common_markup_to_html($instr);
        $this->elementStart('div', 'instructions');
        $this->raw($output);
        $this->elementEnd('div');
    }

    function title()
    {
        // TRANS: Page title.
        return _m('Login with Facebook');
    }

    function showContent() {
        $this->elementStart('fieldset');

        $facebook = Facebookclient::getFacebook();

        $params = array(
          'scope' => 'read_stream,publish_stream,offline_access,user_status,user_location,user_website,email',
          'redirect_uri' => common_local_url('facebookfinishlogin')
        );

        // Degrade to plain link if JavaScript is not available
        $this->elementStart(
            'a',
            array(
                'href' => $facebook->getLoginUrl($params),
                'id'    => 'facebook_button'
            )
        );

        $attrs = array(
            'src' => Plugin::staticPath('FacebookBridge', 'images/login-button.png'),
            // TRANS: Alt text for "Login with Facebook" image.
            'alt'   => _m('Login with Facebook'),
            // TRANS: Title for "Login with Facebook" image.
            'title' => _m('Login with Facebook.')
        );

        $this->element('img', $attrs);

        $this->elementEnd('a');

        $this->elementEnd('fieldset');
    }

    function showLocalNav()
    {
        $nav = new LoginGroupNav($this);
        $nav->show();
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 *  Edit user settings for Facebook
 *
 * PHP version 5
 *
 * LICENCE: This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @category  Settings
 * @package   StatusNet
 * @author    Zach Copley <zach@status.net>
 * @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);
}

/**
 * Edit user settings for Facebook
 *
 * @category Settings
 * @package  StatusNet
 * @author   Zach Copley <zach@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link     http://status.net/
 *
 * @see      SettingsAction
 */
class FacebooksettingsAction extends SettingsAction {
    private $facebook; // Facebook PHP-SDK client obj
    private $flink;
    private $user;

    /**
     * For initializing members of the class.
     *
     * @param array $argarray misc. arguments
     *
     * @return boolean true
     */
    function prepare($args) {
        parent::prepare($args);

        $this->facebook = new Facebook(
            array(
                'appId'  => common_config('facebook', 'appid'),
                'secret' => common_config('facebook', 'secret'),
                'cookie' => true,
            )
        );

        $this->user = common_current_user();

        $this->flink = Foreign_link::getByUserID(
            $this->user->id,
            FACEBOOK_SERVICE
        );

        return true;
    }

    /*
     * Check the sessions token and dispatch
     */
    function handlePost($args) {
        // CSRF protection

        $token = $this->trimmed('token');
        if (!$token || $token != common_session_token()) {
            $this->showForm(
                // TRANS: Client error displayed when the session token does not match or is not given.
                _m('There was a problem with your session token. Try again, please.')
            );
            return;
        }

        if ($this->arg('save')) {
            $this->saveSettings();
        } else if ($this->arg('disconnect')) {
            $this->disconnect();
        }
    }

    /**
     * Returns the page title
     *
     * @return string page title
     */
    function title() {
        // TRANS: Page title for Facebook settings.
        return _m('TITLE','Facebook settings');
    }

    /**
     * Instructions for use
     *
     * @return instructions for use
     */
    function getInstructions() {
        // TRANS: Instructions for Facebook settings.
        return _m('Facebook settings');
    }

    /*
     * Show the settings form if he/she has a link to Facebook
     *
     * @return void
     */
    function showContent() {
        if (!empty($this->flink)) {

            $this->elementStart(
                'form',
                array(
                    'method' => 'post',
                    'id' => 'form_settings_facebook',
                    'class' => 'form_settings',
                    'action' => common_local_url('facebooksettings')
                )
            );

            $this->hidden('token', common_session_token());

            // TRANS: Form note. User is connected to facebook.
            $this->element('p', 'form_note', _m('Connected Facebook user'));

            $this->elementStart('p', array('class' => 'facebook-user-display'));

            $this->element(
                'fb:profile-pic',
                array(
                    'uid' => $this->flink->foreign_id,
                    'size' => 'small',
                    'linked' => 'true',
                    'facebook-logo' => 'true'
                )
            );

            $this->element(
                'fb:name',
                array('uid' => $this->flink->foreign_id, 'useyou' => 'false')
            );

            $this->elementEnd('p');

            $this->elementStart('ul', 'form_data');

            $this->elementStart('li');

            $this->checkbox(
                'noticesync',
                // TRANS: Checkbox label in Facebook settings.
                _m('Publish my notices to Facebook.'),
                ($this->flink) ? ($this->flink->noticesync & FOREIGN_NOTICE_SEND) : true
            );

            $this->elementEnd('li');

            $this->elementStart('li');

            $this->checkbox(
                    'replysync',
                    // TRANS: Checkbox label in Facebook settings.
                    _m('Send "@" replies to Facebook.'),
                    ($this->flink) ? ($this->flink->noticesync & FOREIGN_NOTICE_SEND_REPLY) : true
            );

            $this->elementEnd('li');

            $this->elementStart('li');

            // TRANS: Submit button to save synchronisation settings.
            $this->submit('save', _m('BUTTON', 'Save'));

            $this->elementEnd('li');

            $this->elementEnd('ul');

            $this->elementStart('fieldset');

            // TRANS: Fieldset legend for form to disconnect from Facebook.
            $this->element('legend', null, _m('Disconnect my account from Facebook'));

            if (empty($this->user->password)) {
                $this->elementStart('p', array('class' => 'form_guide'));

                $msg = sprintf(
                    // TRANS: Notice in disconnect from Facebook form if user has no local StatusNet password.
                    _m('Disconnecting your Faceboook would make it impossible to '.
                       'log in! Please [set a password](%s) first.'),
                    common_local_url('passwordsettings')
                );

                $this->raw(common_markup_to_html($msg));
                $this->elementEnd('p');
            } else {
                // @todo FIXME: i18n: This message is not being used.
                // TRANS: Message displayed when initiating disconnect of a StatusNet user
                // TRANS: from a Facebook account. %1$s is the StatusNet site name.
                $msg = sprintf(_m('Keep your %1$s account but disconnect from Facebook. ' .
                                  'You\'ll use your %1$s password to log in.'),
                               common_config('site', 'name')
                );

                // TRANS: Submit button.
                $this->submit('disconnect', _m('BUTTON', 'Disconnect'));
            }

            $this->elementEnd('fieldset');

            $this->elementEnd('form');
         }
    }

    /*
     * Save the user's Facebook settings
     *
     * @return void
     */
    function saveSettings() {
        $noticesync = $this->boolean('noticesync');
        $replysync  = $this->boolean('replysync');

        $original = clone($this->flink);
        $this->flink->set_flags($noticesync, false, $replysync, false);
        $result = $this->flink->update($original);

        if ($result === false) {
            // TRANS: Notice in case saving of synchronisation preferences fail.
            $this->showForm(_m('There was a problem saving your sync preferences.'));
        } else {
            // TRANS: Confirmation that synchronisation settings have been saved into the system.
            $this->showForm(_m('Sync preferences saved.'), true);
        }
    }

    /*
     * Disconnect the user's Facebook account - deletes the Foreign_link
     * and shows the user a success message if all goes well.
     */
    function disconnect() {
        $result = $this->flink->delete();
        $this->flink = null;

        if ($result === false) {
            common_log_db_error($user, 'DELETE', __FILE__);
            // TRANS: Server error displayed when deleting the link to a Facebook account fails.
            $this->serverError(_m('Could not delete link to Facebook.'));
        }

        // TRANS: Confirmation message. StatusNet account was unlinked from Facebook.
        $this->showForm(_m('You have disconnected from Facebook.'), true);
    }
}


code:
<?php
/**
 * Data class for storing notice-to-Facebook-item mappings
 *
 * PHP version 5
 *
 * @category Data
 * @package  StatusNet
 * @author   Zach Copley <zach@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
 * @link     http://status.net/
 *
 * StatusNet - the distributed open-source microblogging tool
 * Copyright (C) 2010, StatusNet, Inc.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

if (!defined('STATUSNET')) {
    exit(1);
}

require_once INSTALLDIR . '/classes/Memcached_DataObject.php';

/**
 * Data class for mapping notices to Facebook stream items
 *
 * Note that notice_id is unique only within a single database; if you
 * want to share this data for some reason, get the notice's URI and use
 * that instead, since it's universally unique.
 *
 * @category Action
 * @package  StatusNet
 * @author   Zach Copley <zach@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
 * @link     http://status.net/
 *
 * @see      DB_DataObject
 */
class Notice_to_item extends Managed_DataObject
{
    public $__table = 'notice_to_item'; // table name
    public $notice_id;                  // int(4)  primary_key not_null
    public $item_id;                    // varchar(191) not null   not 255 because utf8mb4 takes more space
    public $created;                    // datetime

    /**
     * return table definition for DB_DataObject
     *
     * DB_DataObject needs to know something about the table to manipulate
     * instances. This method provides all the DB_DataObject needs to know.
     *
     * @return array array of column definitions
     */
    function table()
    {
        return array(
            'notice_id' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL,
            'item_id'   => DB_DATAOBJECT_STR + DB_DATAOBJECT_NOTNULL,
            'created'   => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL
        );
    }

    static function schemaDef()
    {
        return array(
            new ColumnDef('notice_id', 'integer', null, false, 'PRI'),
            new ColumnDef('item_id', 'varchar', 191, false, 'UNI'),
            new ColumnDef('created', 'datetime',  null, false)
        );
    }

    /**
     * return key definitions for DB_DataObject
     *
     * DB_DataObject needs to know about keys that the table has, since it
     * won't appear in StatusNet's own keys list. In most cases, this will
     * simply reference your keyTypes() function.
     *
     * @return array list of key field names
     */
    function keys()
    {
        return array_keys($this->keyTypes());
    }

    /**
     * return key definitions for Memcached_DataObject
     *
     * Our caching system uses the same key definitions, but uses a different
     * method to get them. This key information is used to store and clear
     * cached data, so be sure to list any key that will be used for static
     * lookups.
     *
     * @return array associative array of key definitions, field name to type:
     *         'K' for primary key: for compound keys, add an entry for each component;
     *         'U' for unique keys: compound keys are not well supported here.
     */
    function keyTypes()
    {
        return array('notice_id' => 'K', 'item_id' => 'U');
    }

    /**
     * Magic formula for non-autoincrementing integer primary keys
     *
     * If a table has a single integer column as its primary key, DB_DataObject
     * assumes that the column is auto-incrementing and makes a sequence table
     * to do this incrementation. Since we don't need this for our class, we
     * overload this method and return the magic formula that DB_DataObject needs.
     *
     * @return array magic three-false array that stops auto-incrementing.
     */
    function sequenceKey()
    {
        return array(false, false, false);
    }

    /**
     * Save a mapping between a notice and a Facebook item
     *
     * @param integer $notice_id ID of the notice in StatusNet
     * @param integer $item_id ID of the stream item on Facebook
     *
     * @return Notice_to_item new object for this value
     */
    static function saveNew($notice_id, $item_id)
    {
        $n2i = Notice_to_item::getKV('notice_id', $notice_id);

        if (!empty($n2i)) {
            return $n2i;
        }

        $n2i = Notice_to_item::getKV('item_id', $item_id);

        if (!empty($n2i)) {
            return $n2i;
        }

        common_debug(
            "Mapping notice {$notice_id} to Facebook item {$item_id}",
            __FILE__
        );

        $n2i = new Notice_to_item();

        $n2i->notice_id = $notice_id;
        $n2i->item_id   = $item_id;
        $n2i->created   = common_sql_now();

        $n2i->insert();

        return $n2i;
    }
}