public function adm_change_bio($args) {
		if (!$this->store_is_logged_in())
			return [AdminStoreError::USER_NOT_LOGGED_IN];

		if (!isset($args['post']))
			return [AdminStoreError::DATA_INCOMPLETE];
		$post = $args['post'];
		$vars = [];
		foreach (['fname', 'site'] as $key) {
			if (!isset($post[$key]))
				continue;
			$val = trim($post[$key]);
			if (!$val)
				continue;
			$vars[$key] = $val;
		}
		if (!$vars)
			# no change
			return [0];

		extract($vars);

		# verify site url value
		if (isset($site) && !self::verify_site_url($site)) {
			$this->logger->warning(
				"Zapmin: chbio: site URL invalid: '$site'.");
			return [AdminStoreError::SITEURL_INVALID];
		}

		$this->store->update('udata', $vars, [
			'uid' => $this->user_data['uid']
		]);

		# also update redis cache
		$expire = $this->store->stmt_fragment('datetime');
		$session = $this->store->query(
			sprintf(
				"SELECT * FROM v_usess " .
				"WHERE token=? AND expire>%s " .
				"LIMIT 1",
				$expire
			), [$this->user_token]);
		# update cache value
		$updated_data = array_merge($this->user_data, $vars);
		if ($session)
			$this->store_redis_cache_write($this->user_token,
				$updated_data, $session['expire']);

		# reset user data but not user token
		$this->user_data = null;
		$this->adm_status();

		# ok
		$this->logger->info(sprintf(
			"Zapmin: chbio: OK: '%s'.",
			$this->user_data['uname']
		));
		return [0];
	}