Exemplo n.º 1
0
 /**
  * Login
  *
  * @param \Zend\Http\Request $request
  * @param \Zend\Http\Response $response
  * @return null|array|\Zend\Http\Response
  */
 public function login(array $options, HttpRequest $request, HttpResponse $response = null)
 {
     if (null === $response) {
         $response = new PhpResponse();
     }
     $session = $this->getSessionContainer();
     $code = $request->getQuery('code');
     if (empty($options['redirect_uri'])) {
         $options['redirect_uri'] = $request->getUri()->getScheme() . '://' . $this->getSiteInfo()->getFulldomain() . $request->getRequestUri();
     }
     if (empty($code)) {
         $session['state'] = String::generateRandom(32);
         $session['redirect_uri'] = $options['redirect_uri'];
         $response->setContent('')->setStatusCode(302)->getHeaders()->clearHeaders()->addHeaderLine('Location', static::DIALOG_URI . '?' . http_build_query(array('client_id' => $options['client_id'], 'redirect_uri' => $options['redirect_uri'], 'state' => $session['state'], 'scope' => 'email')));
         if ($response instanceof PhpResponse) {
             $response->send();
             exit;
         } else {
             return $response;
         }
     }
     $state = $request->getQuery('state');
     if (empty($session['state']) || $state !== $session['state']) {
         return null;
     }
     $client = $this->getHttpClient();
     $params = null;
     @parse_str($client->setMethod('GET')->setUri(static::ACCESS_URI)->setParameterGet(array('client_id' => $options['client_id'], 'redirect_uri' => $session['redirect_uri'], 'client_secret' => $options['client_secret'], 'code' => $code))->send()->getBody(), $params);
     unset($session['state']);
     unset($session['redirect_uri']);
     if (empty($params['access_token'])) {
         return null;
     }
     return @json_decode($client->setMethod('GET')->setUri(static::API_URI)->setParameterGet(array('access_token' => $params['access_token']))->send()->getBody(), true);
 }
Exemplo n.º 2
0
 /**
  * Test strip html
  */
 public function testStripHtml()
 {
     $this->assertEquals("foo\nbar", String::stripHtml('foo<br />bar'));
     $this->assertRegExp("/^foo\n-{3,}\nbar\$/", String::stripHtml('foo<hr noshade />bar'));
     $this->assertRegExp("/^foo ?bar[ \n]?\$/", String::stripHtml('foo<script type="x">xxx</script>bar<style type="y">yyy</style>'));
     $this->assertEquals("foo\nbar", String::stripHtml('<p>foo</p><div>bar</div>'));
     $this->assertEquals("foo & bar\nbaz", String::stripHtml(" foo \t & bar \n\r baz\n "));
 }
Exemplo n.º 3
0
 /**
  * Request an auto-login token
  *
  * @param  string $email
  * @return string token
  */
 public function create($email)
 {
     $store = $this->getCacheStorage();
     do {
         $token = String::generateRandom(self::TOKEN_LENGTH, null, true);
     } while ($store->hasItem($token));
     $store->setItem($token, $email);
     return $token;
 }
Exemplo n.º 4
0
 /**
  * Request a password-change
  *
  * @param   string  $email
  * @return  string  hash
  */
 public function create($email)
 {
     $store = $this->getCacheStorage();
     do {
         $hash = String::generateRandom(self::HASH_LENGTH, null, true);
     } while ($store->hasItem($hash));
     $store->setItem($hash, $email);
     return $hash;
 }
Exemplo n.º 5
0
 /**
  * Hydrate $object with the provided $data.
  *
  * @param  array  $data
  * @param  object $object
  * @return object
  */
 public function hydrate(array $data, $object)
 {
     if (isset($data['properties']) && is_array($data['properties'])) {
         foreach ($data['properties'] as $key => $value) {
             $newKey = String::decamelize($key);
             if ($newKey != $key) {
                 $data['properties'][$newKey] = $value;
                 unset($data['properties'][$key]);
             }
         }
     }
     return $this->getMapper()->hydrate($data, $object);
 }
Exemplo n.º 6
0
 /**
  * Settings admin
  */
 public function indexAction()
 {
     $params = $this->params();
     $request = $this->getRequest();
     $section = $params->fromRoute('section', null);
     $permissions = $this->getPermissionsModel();
     if (empty($section)) {
         $this->getResponse()->setStatusCode(404);
         return;
     }
     $model = $this->getServiceLocator()->get('Grid\\Core\\Model\\Settings\\Model');
     $settings = $model->find($section);
     if (empty($settings)) {
         $this->getResponse()->setStatusCode(404);
         return;
     }
     if (!$permissions->isAllowed('settings.' . $section, 'edit')) {
         $this->getResponse()->setStatusCode(403);
         return;
     }
     /* @var $form \Zend\Form\Form */
     $name = ucfirst(String::camelize($section));
     $form = $this->getServiceLocator()->get('Form')->get('Grid\\Core\\Settings\\' . $name);
     if (empty($form)) {
         $this->getResponse()->setStatusCode(404);
         return;
     }
     $form->setHydrator($model->getMapper())->bind($settings);
     if ($request->isPost()) {
         $form->setData($request->getPost());
         if ($form->isValid() && $settings->save()) {
             $this->messenger()->add('settings.form.all.success', 'settings', Message::LEVEL_INFO);
             return $this->redirect()->toUrl('?refresh');
         } else {
             $this->messenger()->add('settings.form.all.failed', 'settings', Message::LEVEL_ERROR);
         }
     }
     return array('section' => $section, 'name' => $name, 'form' => $form, 'textDomain' => $model->getMapper()->getDefinitions()->getTextDomain($section));
 }
Exemplo n.º 7
0
 /**
  * Add file to uploads
  *
  * @param string $file
  * @param string $dest evaulates in sprintf, adds a random &
  *                     an extension part to the destination
  * @return string
  */
 protected function addFile($file, $dest)
 {
     $file = $this->validateFile($file);
     if (empty($file)) {
         return null;
     }
     $public = realpath('./public');
     if (is_file($public . $file)) {
         if (preg_match('#^/uploads/#', $file)) {
             return $file;
         }
         if (preg_match('#^/tmp/#', $file)) {
             $length = 8;
             $ext = pathinfo($public . $file, PATHINFO_EXTENSION);
             $dest = sprintf($dest, String::generateRandom($length), $ext);
             $schema = $this->getSiteInfo()->getSchema();
             $path = '/uploads/' . $schema . '/' . $dest;
             while (is_file($public . $path)) {
                 if ($length > 24) {
                     @unlink($public . $file);
                     return null;
                 }
                 $dest = sprintf($dest, String::generateRandom(++$length), $ext);
                 $path = '/uploads/' . $schema . '/' . $dest;
             }
             $moveFr = $public . $file;
             $moveTo = $public . $path;
             $movDir = dirname($moveTo);
             if (!is_dir($movDir)) {
                 @mkdir($movDir, 0777, true);
             }
             if (@rename($moveFr, $moveTo)) {
                 return $path;
             }
         }
     }
     return null;
 }
Exemplo n.º 8
0
 /**
  * Performs an authentication attempt
  *
  * @return \Zend\Authentication\Result
  * @throws \Zend\Authentication\Adapter\Exception\ExceptionInterface
  *         If authentication cannot be performed
  */
 public function authenticate()
 {
     $registered = false;
     $model = $this->getModel();
     $settings = $this->getServiceLocator()->get('Grid\\Facebook\\Model\\ApplicationSettings\\AdapterFactory')->factory(array('application' => 'login'));
     $appId = $settings->getSetting('appId');
     $appSecret = $settings->getSetting('appSecret');
     if (empty($appId) || empty($appSecret)) {
         return new Result(Result::FAILURE_UNCATEGORIZED, null, array('appId and/or appSecret not set'));
     }
     $service = $this->getServiceLocator();
     $client = new OAuth\Client($service->get('Zend\\Http\\Client'), $this->getSessionManager(), $service->get('Zork\\Db\\SiteInfo'));
     $data = $client->login(array('client_id' => $appId, 'client_secret' => $appSecret), $service->get('Request'), $service->get('Response'));
     if (empty($data) || empty($data['email'])) {
         return new Result(Result::FAILURE_CREDENTIAL_INVALID, null, array('Cannot parse graph response or email not sent'));
     }
     $email = $data['email'];
     $user = $model->findByEmail($email);
     if (empty($user)) {
         if (!$this->isRegistrationEnabled()) {
             return new Result(Result::FAILURE_IDENTITY_NOT_FOUND, null);
         }
         $displayName = empty($data['name']) ? preg_replace('/@.*$/', '', $email) : $data['name'];
         $i = 1;
         $displayName = UserStructure::trimDisplayName($displayName);
         $originalName = $displayName;
         while (!$model->isDisplayNameAvailable($displayName)) {
             $displayName = $originalName . ' ' . ++$i;
         }
         $user = $model->create(array('confirmed' => true, 'status' => 'active', 'displayName' => $displayName, 'email' => $email, 'locale' => !empty($data['language']) ? $data['language'] : (string) $this->getServiceLocator()->get('Locale'), 'password' => String::generateRandom(10)));
         if ($user->save()) {
             $registered = true;
             $user = $model->findByEmail($email);
         } else {
             return new Result(Result::FAILURE_UNCATEGORIZED, null);
         }
     }
     if (empty($user) || empty($user->id) || $user->isBanned()) {
         return new Result(Result::FAILURE_CREDENTIAL_INVALID, null);
     } else {
         if ($user->isInactive()) {
             $user->makeActive();
             if (!$user->save()) {
                 return new Result(Result::FAILURE_UNCATEGORIZED, null);
             }
         }
     }
     $model->associateIdentity($user->id, empty($data['link']) ? 'urn:facebook:' . (empty($data['id']) ? $email : $data['id']) : $data['link']);
     return new Result(Result::SUCCESS, $user, array('loginWith' => 'facebook', 'registered' => $registered));
 }
Exemplo n.º 9
0
 /**
  * @return  string
  */
 public function getRepresentedTextContent()
 {
     $parts = array();
     if (!empty($this->title)) {
         $parts[] = $this->title;
     }
     if (!empty($this->leadText)) {
         $parts[] = String::stripHtml($this->leadText);
     }
     if (!empty($this->metaDescription)) {
         $parts[] = $this->metaDescription;
     }
     return implode("\n\n", $parts) ?: null;
 }
Exemplo n.º 10
0
 /**
  * Set the message body
  *
  * @param   null|string|\Zend\Mime\Message|\Traversable|array   $body
  * @param   bool                                                $generateText
  * @param   bool                                                $alternative
  * @throws  \Zend\Mail\Exception\InvalidArgumentException
  * @return  \Zork\Mail\Message
  */
 public function setBody($body, $generateText = true, $alternative = true)
 {
     static $mimeAliases = array('text' => Mime\Mime::TYPE_TEXT, 'html' => Mime\Mime::TYPE_HTML, 'alternative' => Mime\Mime::MULTIPART_ALTERNATIVE, 'mixed' => Mime\Mime::MULTIPART_MIXED, 'related' => Mime\Mime::MULTIPART_RELATED);
     if ($body !== null) {
         if (is_scalar($body)) {
             $body = array('text/html' => (string) $body);
         }
         if (!$body instanceof Mime\Message) {
             $message = new Mime\Message();
             if ($body instanceof Mime\Part) {
                 if (empty($body->charset)) {
                     $body->charset = $this->getEncoding();
                 }
                 $message->addPart($body);
             } else {
                 foreach ($body as $type => $content) {
                     if (isset($mimeAliases[$type])) {
                         $type = $mimeAliases[$type];
                     }
                     if ($content instanceof Mime\Message) {
                         /* @var $content \Zend\Mime\Message */
                         if ($content->isMultiPart()) {
                             $mime = $content->getMime();
                             $part = new Mime\Part($content->generateMessage(Headers::EOL));
                             if (!preg_match('#^multipart/#', $type)) {
                                 $type = Mime\Mime::MULTIPART_MIXED;
                             }
                             $part->type = $type;
                             $part->boundary = $mime->boundary();
                         } else {
                             $parts = $content->getParts();
                             $part = reset($parts);
                         }
                     } else {
                         if ($content instanceof Mime\Part) {
                             /* @var $content \Zend\Mime\Part */
                             $part = $content;
                         } else {
                             $part = new Mime\Part($content);
                             $part->type = $type;
                             $part->charset = $this->getEncoding();
                         }
                     }
                     if (empty($part->type)) {
                         $part->type = $type;
                     }
                     if (empty($part->charset)) {
                         $part->charset = $this->getEncoding();
                     }
                     $message->addPart($part);
                 }
             }
             $body = $message;
         }
         /* @var $body \Zend\Mime\Message */
         $partHtml = null;
         $partText = null;
         $parts = $body->getParts();
         foreach ($parts as $part) {
             /* @var $part \Zend\Mime\Part */
             switch ($part->type) {
                 case Mime\Mime::TYPE_HTML:
                     $partHtml = $part;
                     break;
                 case Mime\Mime::TYPE_TEXT:
                     $partText = $part;
                     break;
             }
         }
         if ($generateText && empty($partText) && !empty($partHtml)) {
             $partText = new Mime\Part(String::stripHtml($partHtml->getContent(Headers::EOL), $this->getEncoding()));
             $partText->type = Mime\Mime::TYPE_TEXT;
             $partText->charset = $this->getEncoding();
             array_unshift($parts, $partText);
             $body->setParts($parts);
         }
     }
     parent::setBody($body);
     if ($alternative && $body instanceof Mime\Message && $body->isMultiPart()) {
         $this->getHeaderByName('content-type', 'Zend\\Mail\\Header\\ContentType')->setType(Mime\Mime::MULTIPART_ALTERNATIVE)->addParameter('boundary', $body->getMime()->boundary());
     }
     return $this;
 }
Exemplo n.º 11
0
 /**
  * Get property names
  *
  * @return array
  */
 public function getPropertyNames()
 {
     $result = array();
     foreach ($this->getRawPropertyNames() as $rawName) {
         $result[] = String::camelize($rawName);
     }
     return $result;
 }
Exemplo n.º 12
0
 /**
  * Get RowSet's ID
  *
  * @return string
  */
 public function getId()
 {
     if (empty($this->id)) {
         if ($this->getColumnsUseTranslation()) {
             $id = trim($this->getColumnTranslatePrefix() . '.' . $this->getColumnTranslatePostfix(), '.');
             if (!empty($id)) {
                 return str_replace('.', '_', $id);
             }
         }
         $this->id = String::generateRandom();
     }
     return $this->id;
 }
Exemplo n.º 13
0
 /**
  * @return  string
  */
 public function getRepresentedTextContent()
 {
     return String::stripHtml($this->getRootText()) ?: null;
 }
Exemplo n.º 14
0
 /**
  * Install a package
  */
 public function installAction()
 {
     $extra = array();
     $params = $this->params();
     $vendor = $params->fromRoute('vendor');
     $subname = $params->fromRoute('subname');
     $name = $vendor . '/' . $subname;
     $service = $this->getServiceLocator();
     $model = $service->get('Grid\\Core\\Model\\Package\\Model');
     $forms = $service->get('Form');
     $package = $model->find($name);
     if (!$model->getEnabledPackageCount()) {
         $this->getResponse()->setStatusCode(404);
         return;
     }
     if (empty($package)) {
         $this->getResponse()->setStatusCode(404);
         return;
     }
     if (!$package->canInstall()) {
         $this->getResponse()->setStatusCode(403);
         return;
     }
     $formName = 'Grid\\Core\\Package\\Install\\' . String::camelize($vendor, null, false) . '\\' . String::camelize($subname, null, false);
     /* @var $forms \Zork\Form\FormService */
     if ($forms->has($formName)) {
         /* @var $form \Zork\Form\Form */
         $extraValid = false;
         $request = $this->getRequest();
         $form = $forms->get($formName);
         if ($request->isPost()) {
             $form->setData($request->getPost());
             if ($form->isValid()) {
                 $extra = $form->getData();
                 $extraValid = true;
             }
         }
         if (!$extraValid) {
             $form->add(array('type' => 'Zork\\Form\\Element\\Submit', 'name' => 'submit', 'attributes' => array('value' => 'admin.packages.action.install')));
             return array('name' => $name, 'package' => $package, 'form' => $form);
         }
         if ($form instanceof TransformValues) {
             $extra = $form->transformValues($extra);
         }
     }
     if (!$model->install($package, $extra)) {
         $this->messenger()->add('admin.packages.install.failed', 'admin', Message::LEVEL_ERROR);
         return $this->redirect()->toRoute('Grid\\Core\\Admin\\Package\\View', array('locale' => (string) $this->locale(), 'vendor' => $vendor, 'subname' => $subname));
     }
     return $this->redirect()->toRoute('Grid\\Core\\Admin\\Package\\Update', array('locale' => (string) $this->locale()));
 }
Exemplo n.º 15
0
 /**
  * @param array|\Traversable $variables
  * @param array|\Traversable|string $to
  * @param null|array|\Traversable|string $cc
  * @param null|array|\Traversable|string $bcc
  * @throws Exception\InvalidArgumentException
  * @return void|mixed based on the transport's response
  */
 public function send($variables, $to, $cc = null, $bcc = null)
 {
     if ($variables instanceof Traversable) {
         $variables = ArrayUtils::iteratorToArray($variables);
     }
     if (!is_array($variables)) {
         throw new Exception\InvalidArgumentException('$variables need to be an array ' . '(or instance of \\Traversable) in ' . __METHOD__);
     }
     $html = $this->getTemplateHtml();
     $text = $this->getTemplateText();
     $options = $this->getOptions();
     $options = array_combine(array_keys($options), array_values($options));
     if (empty($variables['site_domain'])) {
         $variables['site_domain'] = $this->getSiteInfo()->getDomain();
     }
     if (empty($variables['site_url'])) {
         $variables['site_url'] = 'http://' . $variables['site_domain'];
     }
     foreach ($variables as $key => &$value) {
         $value = (string) $value;
         if (!empty($value) && $value[0] === '/' && strtolower(substr($key, -4)) === '_url') {
             $value = $variables['site_url'] . $value;
         }
     }
     $options['body'] = array('text/html' => String::template($html, $variables));
     if (!empty($text)) {
         $options['body']['text/plain'] = String::template($text, $variables);
     }
     $options['to'] = $to;
     if (null !== $cc) {
         $options['cc'] = $cc;
     }
     if (null !== $bcc) {
         $options['bcc'] = $bcc;
     }
     return $this->getService()->send($options);
 }
Exemplo n.º 16
0
 /**
  * Upload index
  */
 public function indexAction()
 {
     $auth = $this->getServiceLocator()->get('Zend\\Authentication\\AuthenticationService');
     if (!$auth->hasIdentity()) {
         return array('success' => false);
     }
     $request = $this->getRequest();
     $types = strip_tags($request->getPost('types', $request->getQuery('types')));
     $pattern = strip_tags($request->getPost('pattern', $request->getQuery('pattern')));
     $form = $this->getForm($types, $pattern);
     if ($request->isPost()) {
         $form->setData(ArrayUtils::merge($request->getPost()->toArray(), $request->getFiles()->toArray()));
         if ($form->isValid()) {
             $data = $form->getData();
             $file = $data['file'];
             $ext = pathinfo($file['name'], PATHINFO_EXTENSION);
             if (!is_dir(self::TEMP_PATH)) {
                 @mkdir(self::TEMP_PATH, static::UPLOAD_MOD, true);
             }
             if ('php' === strtolower($ext)) {
                 $ext = 'phps';
             }
             do {
                 $newName = sprintf($pattern, String::generateRandom(null, null, true), $ext);
                 $moveTo = self::TEMP_PATH . DIRECTORY_SEPARATOR . $newName;
             } while (is_file($moveTo));
             if (@move_uploaded_file($file['tmp_name'], $moveTo)) {
                 @chmod($moveTo, static::UPLOAD_MOD);
                 return array('success' => true, 'file' => self::TEMP_URL . '/' . $newName);
             } else {
                 return array('success' => false, 'messages' => array('File move failed' . PHP_EOL . $file['tmp_name'] . PHP_EOL . $moveTo));
             }
         } else {
             return array('success' => false, 'messages' => $form->getMessages());
         }
     }
     return array('form' => $form);
 }
Exemplo n.º 17
0
 /**
  * Guess plugin classes for a name
  *
  * @param   string  $name
  * @return  array
  */
 protected function guessPluginClassesForName($name)
 {
     $camelized = String::camelize($name, '_', false);
     return array('Zork\\View\\Helper\\' . $camelized, 'Zend\\View\\Helper\\' . $camelized, 'Zork\\I18n\\View\\Helper\\' . $camelized, 'Zend\\I18n\\View\\Helper\\' . $camelized, 'Zork\\Form\\View\\Helper\\' . $camelized, 'Zend\\Form\\View\\Helper\\' . $camelized);
 }
Exemplo n.º 18
0
 /**
  * Reflect css properties
  *
  * @param   \Zend\Form\FieldsetInterface    $fieldset
  * @param   string                          $selector
  * @return  \Zend\Form\FieldsetInterface
  */
 protected function reflectCss(FieldsetInterface $fieldset, $selector)
 {
     foreach ($fieldset->getFieldsets() as $subFieldset) {
         if (!$subFieldset instanceof Collection) {
             $this->reflectCss($subFieldset, $selector);
         }
     }
     foreach ($fieldset->getElements() as $name => $element) {
         $types = array_filter(preg_split('/\\s+/', trim($element->getAttribute('data-js-type'))));
         $types[] = 'js.paragraph.reflectCss';
         $element->setAttributes(array('data-js-type' => implode(' ', $types), 'data-js-reflectcss-selector' => $selector, 'data-js-reflectcss-property' => String::decamelize($name)));
     }
     return $fieldset;
 }
Exemplo n.º 19
0
 /**
  * Performs an authentication attempt
  *
  * @return \Zend\Authentication\Result
  * @throws \Zend\Authentication\Adapter\Exception\ExceptionInterface
  *         If authentication cannot be performed
  */
 public function authenticate()
 {
     $registered = false;
     $model = $this->getModel();
     $mode = $this->openid_mode;
     $openId = $this->openid_identity;
     $consumer = new Consumer\FederatedConsumer();
     $ax = new Extension\Ax(array('email' => true, 'firstname' => false, 'lastname' => false, 'language' => false));
     $consumer->setHttpClient($this->getServiceLocator()->get('Zend\\Http\\Client'));
     $success = $mode == 'id_res' ? $consumer->verify((array) $this->getOptions(), $openId, $ax) : $consumer->login($openId, null, null, $ax, $this->getServiceLocator()->get('Response'));
     if (!$success) {
         return new Result(Result::FAILURE_CREDENTIAL_INVALID, null, array((string) $consumer->getError()));
     }
     $data = $ax->getProperties();
     if (empty($data['email'])) {
         return new Result(Result::FAILURE_CREDENTIAL_INVALID, null);
     }
     $email = $data['email'];
     $user = $model->findByEmail($email);
     if (empty($user)) {
         if (!$this->isRegistrationEnabled()) {
             return new Result(Result::FAILURE_IDENTITY_NOT_FOUND, null);
         }
         $displayName = null;
         if (!empty($data['firstname']) && !empty($data['lastname'])) {
             $displayName = $data['firstname'] . ' ' . $data['lastname'];
         } else {
             if (!empty($data['firstname'])) {
                 $displayName = $data['firstname'];
             } else {
                 if (!empty($data['lastname'])) {
                     $displayName = $data['lastname'];
                 } else {
                     $displayName = preg_replace('/@.*$/', '', $email);
                 }
             }
         }
         $i = 1;
         $displayName = UserStructure::trimDisplayName($displayName);
         $originalName = $displayName;
         while (!$model->isDisplayNameAvailable($displayName)) {
             $displayName = $originalName . ' ' . ++$i;
         }
         $user = $model->create(array('confirmed' => true, 'status' => 'active', 'displayName' => $displayName, 'email' => $email, 'locale' => !empty($data['language']) ? $data['language'] : (string) $this->getServiceLocator()->get('Locale'), 'password' => String::generateRandom(10)));
         if ($user->save()) {
             $registered = true;
             $user = $model->findByEmail($email);
         } else {
             return new Result(Result::FAILURE_UNCATEGORIZED, null);
         }
     }
     if (empty($user) || empty($user->id) || $user->isBanned()) {
         return new Result(Result::FAILURE_CREDENTIAL_INVALID, null);
     } else {
         if ($user->isInactive()) {
             $user->makeActive();
             if (!$user->save()) {
                 return new Result(Result::FAILURE_UNCATEGORIZED, null);
             }
         }
     }
     $model->associateIdentity($user->id, $openId);
     return new Result(Result::SUCCESS, $user, array('loginWith' => 'openid', 'registered' => $registered));
 }
Exemplo n.º 20
0
 /**
  * @return  string
  */
 public function getRepresentedTextContent()
 {
     return empty($this->caption) ? $this->alternate : String::stripHtml($this->caption);
 }