2011-02-06 07:36:21 +01:00
|
|
|
<?php
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copyright 2011 Facebook, Inc.
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
class PhabricatorUserSettingsController extends PhabricatorPeopleController {
|
|
|
|
|
|
|
|
private $page;
|
2011-02-28 19:15:42 +01:00
|
|
|
private $accountEditable;
|
2011-02-06 07:36:21 +01:00
|
|
|
|
|
|
|
public function willProcessRequest(array $data) {
|
|
|
|
$this->page = idx($data, 'page');
|
|
|
|
}
|
|
|
|
|
|
|
|
public function processRequest() {
|
|
|
|
|
|
|
|
$request = $this->getRequest();
|
|
|
|
$user = $request->getUser();
|
|
|
|
|
|
|
|
$pages = array(
|
2011-02-20 01:46:14 +01:00
|
|
|
'account' => 'Account',
|
|
|
|
'email' => 'Email',
|
2011-02-06 07:36:21 +01:00
|
|
|
// 'password' => 'Password',
|
|
|
|
'arcanist' => 'Arcanist Certificate',
|
|
|
|
);
|
|
|
|
|
2011-02-22 07:51:34 +01:00
|
|
|
$oauth_providers = PhabricatorOAuthProvider::getAllProviders();
|
|
|
|
foreach ($oauth_providers as $provider) {
|
|
|
|
if (!$provider->isProviderEnabled()) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
$key = $provider->getProviderKey();
|
|
|
|
$name = $provider->getProviderName();
|
|
|
|
$pages[$key] = $name.' Account';
|
|
|
|
}
|
|
|
|
|
2011-02-06 07:36:21 +01:00
|
|
|
if (empty($pages[$this->page])) {
|
|
|
|
$this->page = key($pages);
|
|
|
|
}
|
|
|
|
|
2011-02-28 19:15:42 +01:00
|
|
|
$account_editable = PhabricatorEnv::getEnvConfig('account.editable');
|
|
|
|
$this->accountEditable = $account_editable;
|
|
|
|
|
2011-02-06 07:36:21 +01:00
|
|
|
if ($request->isFormPost()) {
|
|
|
|
switch ($this->page) {
|
2011-02-20 01:46:14 +01:00
|
|
|
case 'email':
|
2011-02-28 19:15:42 +01:00
|
|
|
if (!$account_editable) {
|
|
|
|
return new Aphront400Response();
|
|
|
|
}
|
2011-02-20 01:46:14 +01:00
|
|
|
$user->setEmail($request->getStr('email'));
|
|
|
|
$user->save();
|
|
|
|
return id(new AphrontRedirectResponse())
|
|
|
|
->setURI('/settings/page/email/?saved=true');
|
2011-02-06 07:36:21 +01:00
|
|
|
case 'arcanist':
|
|
|
|
|
|
|
|
if (!$request->isDialogFormPost()) {
|
|
|
|
$dialog = new AphrontDialogView();
|
|
|
|
$dialog->setUser($user);
|
|
|
|
$dialog->setTitle('Really regenerate session?');
|
|
|
|
$dialog->setSubmitURI('/settings/page/arcanist/');
|
|
|
|
$dialog->addSubmitButton('Regenerate');
|
|
|
|
$dialog->addCancelbutton('/settings/page/arcanist/');
|
|
|
|
$dialog->appendChild(
|
|
|
|
'<p>Really destroy the old certificate? Any established '.
|
|
|
|
'sessions will be terminated.');
|
|
|
|
|
|
|
|
return id(new AphrontDialogResponse())
|
|
|
|
->setDialog($dialog);
|
|
|
|
}
|
|
|
|
|
|
|
|
$conn = $user->establishConnection('w');
|
|
|
|
queryfx(
|
|
|
|
$conn,
|
|
|
|
'DELETE FROM %T WHERE userPHID = %s AND type LIKE %>',
|
|
|
|
PhabricatorUser::SESSION_TABLE,
|
|
|
|
$user->getPHID(),
|
|
|
|
'conduit');
|
|
|
|
// This implicitly regenerates the certificate.
|
|
|
|
$user->setConduitCertificate(null);
|
|
|
|
$user->save();
|
|
|
|
return id(new AphrontRedirectResponse())
|
|
|
|
->setURI('/settings/page/arcanist/?regenerated=true');
|
2011-02-06 08:56:06 +01:00
|
|
|
case 'account':
|
2011-02-28 19:15:42 +01:00
|
|
|
if (!$account_editable) {
|
|
|
|
return new Aphront400Response();
|
|
|
|
}
|
|
|
|
|
2011-02-06 08:56:06 +01:00
|
|
|
if (!empty($_FILES['profile'])) {
|
2011-02-10 07:35:00 +01:00
|
|
|
$err = idx($_FILES['profile'], 'error');
|
|
|
|
if ($err != UPLOAD_ERR_NO_FILE) {
|
|
|
|
$file = PhabricatorFile::newFromPHPUpload($_FILES['profile']);
|
|
|
|
$user->setProfileImagePHID($file->getPHID());
|
|
|
|
}
|
2011-02-06 08:56:06 +01:00
|
|
|
}
|
2011-02-06 21:58:01 +01:00
|
|
|
|
2011-02-06 08:56:06 +01:00
|
|
|
$user->save();
|
|
|
|
return id(new AphrontRedirectResponse())
|
|
|
|
->setURI('/settings/page/account/');
|
2011-02-06 07:36:21 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
switch ($this->page) {
|
|
|
|
case 'arcanist':
|
|
|
|
$content = $this->renderArcanistCertificateForm();
|
|
|
|
break;
|
2011-02-06 08:56:06 +01:00
|
|
|
case 'account':
|
|
|
|
$content = $this->renderAccountForm();
|
|
|
|
break;
|
2011-02-20 01:46:14 +01:00
|
|
|
case 'email':
|
|
|
|
$content = $this->renderEmailForm();
|
|
|
|
break;
|
2011-02-06 07:36:21 +01:00
|
|
|
default:
|
2011-02-22 07:51:34 +01:00
|
|
|
if (empty($pages[$this->page])) {
|
|
|
|
return new Aphront404Response();
|
|
|
|
}
|
|
|
|
$content = $this->renderOAuthForm($oauth_providers[$this->page]);
|
2011-02-06 07:36:21 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$sidenav = new AphrontSideNavView();
|
|
|
|
foreach ($pages as $page => $name) {
|
|
|
|
$sidenav->addNavItem(
|
|
|
|
phutil_render_tag(
|
|
|
|
'a',
|
|
|
|
array(
|
|
|
|
'href' => '/settings/page/'.$page.'/',
|
|
|
|
'class' => ($page == $this->page)
|
|
|
|
? 'aphront-side-nav-selected'
|
|
|
|
: null,
|
|
|
|
),
|
|
|
|
phutil_escape_html($name)));
|
|
|
|
}
|
|
|
|
|
|
|
|
$sidenav->appendChild($content);
|
|
|
|
|
|
|
|
return $this->buildStandardPageResponse(
|
|
|
|
$sidenav,
|
|
|
|
array(
|
|
|
|
'title' => 'Account Settings',
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
private function renderArcanistCertificateForm() {
|
|
|
|
$request = $this->getRequest();
|
|
|
|
$user = $request->getUser();
|
|
|
|
|
|
|
|
if ($request->getStr('regenerated')) {
|
|
|
|
$notice = new AphrontErrorView();
|
|
|
|
$notice->setSeverity(AphrontErrorView::SEVERITY_NOTICE);
|
|
|
|
$notice->setTitle('Certificate Regenerated');
|
|
|
|
$notice->appendChild(
|
|
|
|
'<p>Your old certificate has been destroyed and you have been issued '.
|
|
|
|
'a new certificate. Sessions established under the old certificate '.
|
|
|
|
'are no longer valid.</p>');
|
|
|
|
$notice = $notice->render();
|
|
|
|
} else {
|
|
|
|
$notice = null;
|
|
|
|
}
|
|
|
|
|
2011-03-12 00:34:55 +01:00
|
|
|
$host = PhabricatorEnv::getEnvConfig('phabricator.base-uri') . 'api/';
|
|
|
|
$conduit_setting = sprintf(
|
|
|
|
' %s: {'."\n".
|
|
|
|
' "user" : %s,'."\n".
|
|
|
|
' "cert" : %s'."\n".
|
|
|
|
' }'."\n",
|
|
|
|
json_encode($host),
|
|
|
|
json_encode($user->getUserName()),
|
|
|
|
json_encode($user->getConduitCertificate()));
|
|
|
|
|
2011-02-06 07:36:21 +01:00
|
|
|
$cert_form = new AphrontFormView();
|
|
|
|
$cert_form
|
|
|
|
->setUser($user)
|
|
|
|
->appendChild(
|
2011-03-12 00:34:55 +01:00
|
|
|
'<p class="aphront-form-instructions">Copy and paste the host info '.
|
|
|
|
'including the certificate into your <tt>~/.arcrc</tt> in the "hosts" '.
|
|
|
|
'session to enable Arcanist to authenticate against this host.</p>')
|
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormMarkupControl())
|
|
|
|
->setControlStyle('white-space: pre; font-family: monospace')
|
|
|
|
->setValue(
|
|
|
|
'{'."\n".
|
|
|
|
' ...'."\n".
|
|
|
|
' "hosts" : {'."\n"))
|
2011-02-06 07:36:21 +01:00
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormTextAreaControl())
|
2011-03-12 00:34:55 +01:00
|
|
|
->setLabel('Credentials')
|
2011-02-06 07:36:21 +01:00
|
|
|
->setHeight(AphrontFormTextAreaControl::HEIGHT_SHORT)
|
2011-03-12 00:34:55 +01:00
|
|
|
->setControlStyle('font-family: monospace')
|
|
|
|
->setValue($conduit_setting))
|
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormStaticControl())
|
|
|
|
->setControlStyle('white-space: pre; font-family: monospace')
|
|
|
|
->setValue(
|
|
|
|
' }'."\n".
|
|
|
|
' ...'."\n".
|
|
|
|
'}'));
|
2011-02-06 07:36:21 +01:00
|
|
|
|
|
|
|
$cert = new AphrontPanelView();
|
|
|
|
$cert->setHeader('Arcanist Certificate');
|
|
|
|
$cert->appendChild($cert_form);
|
|
|
|
$cert->setWidth(AphrontPanelView::WIDTH_FORM);
|
|
|
|
|
|
|
|
$regen_form = new AphrontFormView();
|
|
|
|
$regen_form
|
|
|
|
->setUser($user)
|
|
|
|
->setWorkflow(true)
|
|
|
|
->setAction('/settings/page/arcanist/')
|
|
|
|
->appendChild(
|
|
|
|
'<p class="aphront-form-instructions">You can regenerate this '.
|
|
|
|
'certificate, which will invalidate the old certificate and create '.
|
|
|
|
'a new one.</p>')
|
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormSubmitControl())
|
|
|
|
->setValue('Regenerate Certificate'));
|
|
|
|
|
|
|
|
$regen = new AphrontPanelView();
|
|
|
|
$regen->setHeader('Regenerate Certificate');
|
|
|
|
$regen->appendChild($regen_form);
|
|
|
|
$regen->setWidth(AphrontPanelView::WIDTH_FORM);
|
|
|
|
|
|
|
|
return $notice.$cert->render().$regen->render();
|
|
|
|
}
|
2011-02-06 21:58:01 +01:00
|
|
|
|
2011-02-06 08:56:06 +01:00
|
|
|
private function renderAccountForm() {
|
|
|
|
$request = $this->getRequest();
|
|
|
|
$user = $request->getUser();
|
2011-02-06 21:58:01 +01:00
|
|
|
|
2011-02-06 08:56:06 +01:00
|
|
|
$img_src = PhabricatorFileURI::getViewURIForPHID(
|
|
|
|
$user->getProfileImagePHID());
|
2011-02-06 21:58:01 +01:00
|
|
|
|
2011-02-28 19:15:42 +01:00
|
|
|
$editable = $this->accountEditable;
|
|
|
|
|
2011-02-06 08:56:06 +01:00
|
|
|
$form = new AphrontFormView();
|
|
|
|
$form
|
|
|
|
->setUser($user)
|
|
|
|
->setEncType('multipart/form-data')
|
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormStaticControl())
|
|
|
|
->setLabel('Username')
|
|
|
|
->setValue($user->getUsername()))
|
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormTextControl())
|
|
|
|
->setLabel('Real Name')
|
2011-02-28 19:15:42 +01:00
|
|
|
->setValue($user->getRealName())
|
|
|
|
->setDisabled(!$editable))
|
2011-02-06 08:56:06 +01:00
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormMarkupControl())
|
|
|
|
->setValue('<hr />'))
|
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormMarkupControl())
|
|
|
|
->setLabel('Profile Image')
|
|
|
|
->setValue(
|
|
|
|
phutil_render_tag(
|
|
|
|
'img',
|
|
|
|
array(
|
|
|
|
'src' => $img_src,
|
2011-02-28 19:15:42 +01:00
|
|
|
))));
|
|
|
|
|
|
|
|
if ($editable) {
|
|
|
|
$form
|
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormFileControl())
|
|
|
|
->setLabel('Change Image')
|
|
|
|
->setName('profile')
|
|
|
|
->setCaption('Upload a 50x50px image.'))
|
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormMarkupControl())
|
|
|
|
->setValue('<hr />'))
|
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormSubmitControl())
|
|
|
|
->setValue('Save'));
|
|
|
|
}
|
2011-02-06 21:58:01 +01:00
|
|
|
|
2011-02-06 08:56:06 +01:00
|
|
|
$panel = new AphrontPanelView();
|
|
|
|
$panel->setHeader('Profile Settings');
|
|
|
|
$panel->setWidth(AphrontPanelView::WIDTH_FORM);
|
|
|
|
$panel->appendChild($form);
|
2011-02-06 21:58:01 +01:00
|
|
|
|
2011-02-06 08:56:06 +01:00
|
|
|
return $panel->render();
|
|
|
|
}
|
2011-02-06 07:36:21 +01:00
|
|
|
|
2011-02-20 01:46:14 +01:00
|
|
|
private function renderEmailForm() {
|
|
|
|
$request = $this->getRequest();
|
|
|
|
$user = $request->getUser();
|
|
|
|
|
2011-02-28 19:15:42 +01:00
|
|
|
$editable = $this->accountEditable;
|
|
|
|
|
2011-02-20 01:46:14 +01:00
|
|
|
if ($request->getStr('saved')) {
|
|
|
|
$notice = new AphrontErrorView();
|
|
|
|
$notice->setSeverity(AphrontErrorView::SEVERITY_NOTICE);
|
|
|
|
$notice->setTitle('Changed Saved');
|
|
|
|
$notice->appendChild('<p>Your changes have been saved.</p>');
|
|
|
|
$notice = $notice->render();
|
|
|
|
} else {
|
|
|
|
$notice = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
$form = new AphrontFormView();
|
|
|
|
$form
|
|
|
|
->setUser($user)
|
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormTextControl())
|
|
|
|
->setLabel('Email')
|
|
|
|
->setName('email')
|
2011-02-28 19:15:42 +01:00
|
|
|
->setDisabled(!$editable)
|
2011-02-20 01:46:14 +01:00
|
|
|
->setCaption(
|
|
|
|
'Note: there is no email validation yet; double-check your '.
|
|
|
|
'typing.')
|
2011-02-28 19:15:42 +01:00
|
|
|
->setValue($user->getEmail()));
|
|
|
|
|
|
|
|
if ($editable) {
|
|
|
|
$form
|
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormSubmitControl())
|
|
|
|
->setValue('Save'));
|
|
|
|
}
|
2011-02-20 01:46:14 +01:00
|
|
|
|
|
|
|
$panel = new AphrontPanelView();
|
|
|
|
$panel->setHeader('Email Settings');
|
|
|
|
$panel->setWidth(AphrontPanelView::WIDTH_FORM);
|
|
|
|
$panel->appendChild($form);
|
|
|
|
|
|
|
|
return $notice.$panel->render();
|
|
|
|
}
|
|
|
|
|
2011-02-22 07:51:34 +01:00
|
|
|
private function renderOAuthForm(PhabricatorOAuthProvider $provider) {
|
|
|
|
|
|
|
|
$request = $this->getRequest();
|
|
|
|
$user = $request->getUser();
|
|
|
|
|
|
|
|
$notice = null;
|
|
|
|
|
|
|
|
$provider_name = $provider->getProviderName();
|
|
|
|
$provider_key = $provider->getProviderKey();
|
|
|
|
|
|
|
|
$oauth_info = id(new PhabricatorUserOAuthInfo())->loadOneWhere(
|
|
|
|
'userID = %d AND oauthProvider = %s',
|
|
|
|
$user->getID(),
|
|
|
|
$provider->getProviderKey());
|
|
|
|
|
|
|
|
$form = new AphrontFormView();
|
|
|
|
$form
|
|
|
|
->setUser($user);
|
|
|
|
|
|
|
|
$forms = array();
|
|
|
|
$forms[] = $form;
|
|
|
|
if (!$oauth_info) {
|
|
|
|
$form
|
|
|
|
->appendChild(
|
|
|
|
'<p class="aphront-form-instructions">There is currently no '.
|
|
|
|
$provider_name.' account linked to your Phabricator account. You '.
|
|
|
|
'can link an account, which will allow you to use it to log into '.
|
|
|
|
'Phabricator.</p>');
|
|
|
|
|
|
|
|
switch ($provider_key) {
|
|
|
|
case PhabricatorOAuthProvider::PROVIDER_GITHUB:
|
|
|
|
$form->appendChild(
|
|
|
|
'<p class="aphront-form-instructions">Additionally, you must '.
|
|
|
|
'link your Github account before Phabricator can access any '.
|
|
|
|
'information about hosted repositories.</p>');
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
$auth_uri = $provider->getAuthURI();
|
|
|
|
$client_id = $provider->getClientID();
|
|
|
|
$redirect_uri = $provider->getRedirectURI();
|
|
|
|
|
|
|
|
$form
|
|
|
|
->setAction($auth_uri)
|
|
|
|
->setMethod('GET')
|
|
|
|
->addHiddenInput('redirect_uri', $redirect_uri)
|
|
|
|
->addHiddenInput('client_id', $client_id)
|
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormSubmitControl())
|
|
|
|
->setValue('Link '.$provider_name." Account \xC2\xBB"));
|
|
|
|
} else {
|
|
|
|
$form
|
|
|
|
->appendChild(
|
|
|
|
'<p class="aphront-form-instructions">Your account is linked with '.
|
|
|
|
'a '.$provider_name.' account. You may use your '.$provider_name.' '.
|
|
|
|
'credentials to log into Phabricator.</p>')
|
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormStaticControl())
|
|
|
|
->setLabel($provider_name.' ID')
|
2011-02-22 19:24:49 +01:00
|
|
|
->setValue($oauth_info->getOAuthUID()))
|
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormStaticControl())
|
|
|
|
->setLabel($provider_name.' Name')
|
|
|
|
->setValue($oauth_info->getAccountName()))
|
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormStaticControl())
|
|
|
|
->setLabel($provider_name.' URI')
|
|
|
|
->setValue($oauth_info->getAccountURI()));
|
2011-02-22 07:51:34 +01:00
|
|
|
|
2011-02-28 04:47:22 +01:00
|
|
|
if (!$provider->isProviderLinkPermanent()) {
|
|
|
|
$unlink = 'Unlink '.$provider_name.' Account';
|
|
|
|
$unlink_form = new AphrontFormView();
|
|
|
|
$unlink_form
|
|
|
|
->setUser($user)
|
|
|
|
->appendChild(
|
|
|
|
'<p class="aphront-form-instructions">You may unlink this account '.
|
|
|
|
'from your '.$provider_name.' account. This will prevent you from '.
|
|
|
|
'logging in with your '.$provider_name.' credentials.</p>')
|
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormSubmitControl())
|
|
|
|
->addCancelButton('/oauth/'.$provider_key.'/unlink/', $unlink));
|
|
|
|
$forms['Unlink Account'] = $unlink_form;
|
|
|
|
}
|
2011-02-22 19:24:49 +01:00
|
|
|
|
|
|
|
$expires = $oauth_info->getTokenExpires();
|
|
|
|
if ($expires) {
|
|
|
|
if ($expires <= time()) {
|
|
|
|
$expires = "Expired";
|
|
|
|
} else {
|
|
|
|
$expires = phabricator_format_timestamp($expires);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$expires = 'No Information Available';
|
|
|
|
}
|
|
|
|
|
|
|
|
$scope = $oauth_info->getTokenScope();
|
|
|
|
if (!$scope) {
|
|
|
|
$scope = 'No Information Available';
|
|
|
|
}
|
|
|
|
|
|
|
|
$status = $oauth_info->getTokenStatus();
|
|
|
|
$status = PhabricatorUserOAuthInfo::getReadableTokenStatus($status);
|
|
|
|
|
|
|
|
$token_form = new AphrontFormView();
|
|
|
|
$token_form
|
|
|
|
->setUser($user)
|
|
|
|
->appendChild(
|
|
|
|
'<p class="aphront-from-instructions">insert rap about tokens</p>')
|
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormStaticControl())
|
|
|
|
->setLabel('Token Status')
|
|
|
|
->setValue($status))
|
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormStaticControl())
|
|
|
|
->setLabel('Expires')
|
|
|
|
->setValue($expires))
|
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormStaticControl())
|
|
|
|
->setLabel('Scope')
|
|
|
|
->setValue($scope));
|
|
|
|
|
|
|
|
$forms['Account Token Information'] = $token_form;
|
2011-02-22 07:51:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
$panel = new AphrontPanelView();
|
|
|
|
$panel->setHeader($provider_name.' Account Settings');
|
|
|
|
$panel->setWidth(AphrontPanelView::WIDTH_FORM);
|
|
|
|
foreach ($forms as $name => $form) {
|
|
|
|
if ($name) {
|
|
|
|
$panel->appendChild('<br /><br /><h1>'.$name.'</h1>');
|
|
|
|
}
|
|
|
|
$panel->appendChild($form);
|
|
|
|
}
|
|
|
|
|
|
|
|
return $notice.$panel->render();
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2011-02-06 07:36:21 +01:00
|
|
|
}
|