Seems, you need to expose some pages. Cim's code will help you in exposing actions to guests. You may be looking for http://learn.elgg.org/en/1.10/guides/walled-garden.html
There's the "public" access level (site content item specific) and there are "public pages" (in context of walled garden, i.e. pages a visitor who's not logged in is allowed to see on a walled garden site).
I think this topic is about a "public pages" issue with Tidypics on a walled garden site. Though the "public" access level also is to kept in mind here.
First thing: make Tidypics pages (photos / albums etc.) "public pages". This can be achived by using the 'public_pages', 'walled_garden' plugin hook.
Define a plugin hook handler in a init function (for example in a collected customizations plugin):
elgg_register_plugin_hook_handler('public_pages', 'walled_garden', 'public_photo_pages');
Then also add the callback function to the same start.php:
function public_photo_pages($hook, $handler, $return, $params) {
$pages = array('photos/.*');
return array_merge($pages, $return);
}
This makes all Tidypic pages public pages. If you want to restrict it to specific pages, you need to add each of these specific pages in the $pages array in the callback function instead of using the ".*" wildcard. Image-based (or even user-based) restrictions might be difficult to define though. Also, the access level of the images (or more precisely the access level of the album which defines the access level of its images) define which images can be seen even if the page is a public page. This means that not only the page needs to be public but the album needs to have "public" as access level. If the access level is any different, the images / albums will stay invisible for the anonymous visitors. Group albums are another matter. If the group access is not "public" the group albums/images will stay hidden even if the group album has a public access level.
Many thanks to you! Very clear explanation and hook handles, this helps me a lot.
Works Perfect!
PS: we love Tidypics!
Thank you all for your support!
There's no plugin option to prevent upscaling of small images.
Tidypics creates 3 resized images: tiny (by default 60x60), small (153x153) and large (600x600). Additionally, the original image file is used when viewing the image in a lightbox popup.
The tiny and small images are not only resized but also cropped (made squared). As they are used at different places (e.g. album list view, widgets etc.) I wouldn't suggest to change the cropping. Changing the size these versions of the images should be created with is possible in the Tidypics plugin settings. But changing the default size will very likely result in adjustments in the site's theme to become necessary for the page layouts to look good. I also don't think it would make much sense in making the size of these thumbnails even smaller as it would get really difficult to see anything on them at all then. Also, if upscaling would really occur for even these small sizes I wouldn't expect that the size would have to get increased much.
The large size of the images is very likely the size you are referring to (this size is used on the full view pages of a single image). Default size is 600x600 with keeping the aspect ratio. On downsizing this means the width or height (whichever is larger) is reduced to 600 px and the other dimension is reduced with the same ratio (resulting it to be smaller than 600 px then). On upscalling the process would be the other way round. The larger dimension of width or height is increased until it's 600 px with the other dimension increased for the same ratio.
The whole resizing happens in mod/tidypics/lib/resize.php. If you don't want upscaling to happen, you would have to add some code for comparing the original image sizes with the target image sizes and in case the original image dimensions are smaller set the target image size to the original image size. In this case a copy of the original image would get created with the same dimensions. Most likely it would be even better to omit the re-size process for these cases (some - even mostly not noticeable - quality loss always happens on the resizing) and simply return the original image without performing the resize at all.
As Tidypics can deal with 3 different image libraries you would have to prevent the upscaling to happen for any of the 3 libraries used but at least for the library you do use on your site. The re-size code for all 3 libraries is in mod/tidypics/lib/resize.php.
iionly,
Thank you for such a thorough explanation. I am using imagemagick at the moment so will take a look in the file to see if I can find anything.
I have kept all the faults for Tidypics and don't intend to change them. The upscaling is not a deal breaker.
Again thank you for your time.
Drew
Can you reconstruct what has gone wrong when using the script? I would need to know what might have gone wrong to be able to tell you how to avoid an error occuring when using the script.
But I guess more important is now getting your site working again:
The exception number doesn't tell what error occurs but is only telling when it occured (it's a timestamp). You would need to look into the server logs (Apache logs, php logs) to find out what the problem is. There should be an entry with the corresponding exception number telling you more details about the error.
Re-installing the files in Elgg's install directory shouldn't have been necessary as there wouldn't have been any changes there by using the script. Now after re-installing the files there might be the settings file (engine/settings.php) missing which gets created during installation only. Within this file the database connection details (database username, database name, password etc) are saved. If the file is missing or does not contain the correct database details there would be an error for sure. If that's the case you need to create settings.php manually again (copy+rename settings.example.php) and enter the correct database details without the {{ }} in the quotations marks. Default of dbhost is 'localhost' and default dbprefix is 'elgg_'. If you haven't entered something else during installation you would have to use these default values and of course the correct dbuser, dbpass and dbname.
If this isn't the reason for the problem you might need to re-install the database backup again. Maybe something has gone wrong here the first time.
Sorry to bother you so much.
Very useful your advise on settings.php ! I have updated all the new settings manually.
Following errors in the Elgg database:
Fatal error: Uncaught exception 'DatabaseException' with message 'Table 'arteosak_elgg.elgg_users_sessions' doesn't exist QUERY: REPLACE INTO elgg_users_sessions (session, ts, data) VALUES ('2b058e5278cb0bde4d8573d392c82f53', '1414609302', '')' in /home/arteosak/public_html/engine/classes/Elgg/Database.php:367 Stack trace: #0 /home/arteosak/public_html/engine/classes/Elgg/Database.php(213): Elgg_Database->executeQuery('REPLACE INTO el...', Resource id #61) #1 /home/arteosak/public_html/engine/classes/Elgg/Http/DatabaseSessionHandler.php(59): Elgg_Database->insertData('REPLACE INTO el...') #2 [internal function]: Elgg_Http_DatabaseSessionHandler->write('2b058e5278cb0bd...', '') #3 {main} thrown in /home/arteosak/public_html/engine/classes/Elgg/Database.php on line 367
Since file upload is not possible I paste the database .php file as follows:
<?php
/**
* An object representing a single Elgg database.
*
* WARNING: THIS API IS IN FLUX. PLUGIN AUTHORS SHOULD NOT USE. See lib/database.php instead.
*
* @access private
*
* @package Elgg.Core
* @subpackage Database
*/
class Elgg_Database {
/** @var string $tablePrefix Prefix for database tables */
private $tablePrefix;
/** @var resource[] $dbLinks Database connection resources */
private $dbLinks = array();
/** @var int $queryCount The number of queries made */
private $queryCount = 0;
/**
* Query cache for select queries.
*
* Queries and their results are stored in this cache as:
* <code>
* $DB_QUERY_CACHE[query hash] => array(result1, result2, ... resultN)
* </code>
* @see Elgg_Database::getResults() for details on the hash.
*
* @var Elgg_Cache_LRUCache $queryCache The cache
*/
private $queryCache = null;
/**
* @var int $queryCacheSize The number of queries to cache
*/
private $queryCacheSize = 50;
/**
* Queries are saved to an array and executed using
* a function registered by register_shutdown_function().
*
* Queries are saved as an array in the format:
* <code>
* $this->delayedQueries[] = array(
* 'q' => string $query,
* 'l' => string $query_type,
* 'h' => string $handler // a callback function
* );
* </code>
*
* @var array $delayedQueries Queries to be run during shutdown
*/
private $delayedQueries = array();
/** @var bool $installed Is the database installed? */
private $installed = false;
/** @var Elgg_Database_Config $config Database configuration */
private $config;
/** @var Elgg_Logger $logger The logger */
private $logger;
/**
* Constructor
*
* @param Elgg_Database_Config $config Database configuration
* @param Elgg_Logger $logger The logger
*/
public function __construct(Elgg_Database_Config $config, Elgg_Logger $logger) {
$this->logger = $logger;
$this->config = $config;
$this->tablePrefix = $config->getTablePrefix();
$this->enableQueryCache();
}
/**
* Gets (if required, also creates) a database link resource.
*
* The database link resources are created by
* {@link Elgg_Database::setupConnections()}, which is called if no links exist.
*
* @param string $type The type of link we want: "read", "write" or "readwrite".
*
* @return resource Database link
* @throws DatabaseException
* @todo make protected once we get rid of get_db_link()
*/
public function getLink($type) {
if (isset($this->dbLinks[$type])) {
return $this->dbLinks[$type];
} else if (isset($this->dbLinks['readwrite'])) {
return $this->dbLinks['readwrite'];
} else {
$this->setupConnections();
return $this->getLink($type);
}
}
/**
* Establish database connections
*
* If the configuration has been set up for multiple read/write databases, set those
* links up separately; otherwise just create the one database link.
*
* @return void
* @throws DatabaseException
*/
public function setupConnections() {
if ($this->config->isDatabaseSplit()) {
$this->establishLink('read');
$this->establishLink('write');
} else {
$this->establishLink('readwrite');
}
}
/**
* Establish a connection to the database server
*
* Connect to the database server and use the Elgg database for a particular database link
*
* @param string $dblinkname The type of database connection. Used to identify the
* resource: "read", "write", or "readwrite".
*
* @return void
* @throws DatabaseException
*/
public function establishLink($dblinkname = "readwrite") {
$conf = $this->config->getConnectionConfig($dblinkname);
// Connect to database
$this->dbLinks[$dblinkname] = mysql_connect($conf['host'], $conf['user'], $conf['password'], true);
if (!$this->dbLinks[$dblinkname]) {
$msg = "Elgg couldn't connect to the database using the given credentials. Check the settings file.";
throw new DatabaseException($msg);
}
if (!mysql_select_db($conf['database'], $this->dbLinks[$dblinkname])) {
$msg = "Elgg couldn't select the database '{$conf['database']}'. Please check that the database is created and you have access to it.";
throw new DatabaseException($msg);
}
// Set DB for UTF8
mysql_query("SET NAMES utf8");
}
/**
* Retrieve rows from the database.
*
* Queries are executed with {@link Elgg_Database::executeQuery()} and results
* are retrieved with {@link mysql_fetch_object()}. If a callback
* function $callback is defined, each row will be passed as a single
* argument to $callback. If no callback function is defined, the
* entire result set is returned as an array.
*
* @param mixed $query The query being passed.
* @param string $callback Optionally, the name of a function to call back to on each row
*
* @return array An array of database result objects or callback function results. If the query
* returned nothing, an empty array.
* @throws DatabaseException
*/
public function getData($query, $callback = '') {
return $this->getResults($query, $callback, false);
}
/**
* Retrieve a single row from the database.
*
* Similar to {@link Elgg_Database::getData()} but returns only the first row
* matched. If a callback function $callback is specified, the row will be passed
* as the only argument to $callback.
*
* @param mixed $query The query to execute.
* @param string $callback A callback function
*
* @return mixed A single database result object or the result of the callback function.
* @throws DatabaseException
*/
public function getDataRow($query, $callback = '') {
return $this->getResults($query, $callback, true);
}
/**
* Insert a row into the database.
*
* @note Altering the DB invalidates all queries in the query cache.
*
* @param mixed $query The query to execute.
*
* @return int|false The database id of the inserted row if a AUTO_INCREMENT field is
* defined, 0 if not, and false on failure.
* @throws DatabaseException
*/
public function insertData($query) {
$this->logger->log("DB query $query", Elgg_Logger::INFO);
$dblink = $this->getLink('write');
$this->invalidateQueryCache();
if ($this->executeQuery("$query", $dblink)) {
return mysql_insert_id($dblink);
}
return false;
}
/**
* Update the database.
*
* @note Altering the DB invalidates all queries in the query cache.
*
* @param string $query The query to run.
* @param bool $getNumRows Return the number of rows affected (default: false)
*
* @return bool|int
* @throws DatabaseException
*/
public function updateData($query, $getNumRows = false) {
$this->logger->log("DB query $query", Elgg_Logger::INFO);
$dblink = $this->getLink('write');
$this->invalidateQueryCache();
if ($this->executeQuery("$query", $dblink)) {
if ($getNumRows) {
return mysql_affected_rows($dblink);
} else {
return true;
}
}
return false;
}
/**
* Delete data from the database
*
* @note Altering the DB invalidates all queries in query cache.
*
* @param string $query The SQL query to run
*
* @return int|false The number of affected rows or false on failure
* @throws DatabaseException
*/
public function deleteData($query) {
$this->logger->log("DB query $query", Elgg_Logger::INFO);
$dblink = $this->getLink('write');
$this->invalidateQueryCache();
if ($this->executeQuery("$query", $dblink)) {
return mysql_affected_rows($dblink);
}
return false;
}
/**
* Handles queries that return results, running the results through a
* an optional callback function. This is for R queries (from CRUD).
*
* @param string $query The select query to execute
* @param string $callback An optional callback function to run on each row
* @param bool $single Return only a single result?
*
* @return array An array of database result objects or callback function results. If the query
* returned nothing, an empty array.
* @throws DatabaseException
*/
protected function getResults($query, $callback = null, $single = false) {
// Since we want to cache results of running the callback, we need to
// need to namespace the query with the callback and single result request.
// https://github.com/elgg/elgg/issues/4049
$callback_hash = is_object($callback) ? spl_object_hash($callback) : (string)$callback;
$hash = $callback_hash . (int)$single . $query;
// Is cached?
if ($this->queryCache) {
if (isset($this->queryCache[$hash])) {
$this->logger->log("DB query $query results returned from cache (hash: $hash)", Elgg_Logger::INFO);
return $this->queryCache[$hash];
}
}
$dblink = $this->getLink('read');
$return = array();
if ($result = $this->executeQuery("$query", $dblink)) {
// test for callback once instead of on each iteration.
// @todo check profiling to see if this needs to be broken out into
// explicit cases instead of checking in the interation.
$is_callable = is_callable($callback);
while ($row = mysql_fetch_object($result)) {
if ($is_callable) {
$row = call_user_func($callback, $row);
}
if ($single) {
$return = $row;
break;
} else {
$return[] = $row;
}
}
}
if (empty($return)) {
$this->logger->log("DB query $query returned no results.", Elgg_Logger::INFO);
}
// Cache result
if ($this->queryCache) {
$this->queryCache[$hash] = $return;
$this->logger->log("DB query $query results cached (hash: $hash)", Elgg_Logger::INFO);
}
return $return;
}
/**
* Execute a query.
*
* $query is executed via {@link mysql_query()}. If there is an SQL error,
* a {@link DatabaseException} is thrown.
*
* @param string $query The query
* @param resource $dblink The DB link
*
* @return resource|bool The result of mysql_query()
* @throws DatabaseException
* @todo should this be public?
*/
public function executeQuery($query, $dblink) {
if ($query == null) {
throw new DatabaseException("Query cannot be null");
}
if (!is_resource($dblink)) {
throw new DatabaseException("Connection to database was lost.");
}
$this->queryCount++;
$result = mysql_query($query, $dblink);
if (mysql_errno($dblink)) {
throw new DatabaseException(mysql_error($dblink) . "\n\n QUERY: $query");
}
return $result;
}
/**
* Runs a full database script from disk.
*
* The file specified should be a standard SQL file as created by
* mysqldump or similar. Statements must be terminated with ;
* and a newline character (\n or \r\n) with only one statement per line.
*
* The special string 'prefix_' is replaced with the database prefix
* as defined in {@link $this->tablePrefix}.
*
* @warning Errors do not halt execution of the script. If a line
* generates an error, the error message is saved and the
* next line is executed. After the file is run, any errors
* are displayed as a {@link DatabaseException}
*
* @param string $scriptlocation The full path to the script
*
* @return void
* @throws DatabaseException
*/
public function runSqlScript($scriptlocation) {
$script = file_get_contents($scriptlocation);
if ($script) {
$errors = array();
// Remove MySQL -- style comments
$script = preg_replace('/\-\-.*\n/', '', $script);
// Statements must end with ; and a newline
$sql_statements = preg_split('/;[\n\r]+/', $script);
foreach ($sql_statements as $statement) {
$statement = trim($statement);
$statement = str_replace("prefix_", $this->tablePrefix, $statement);
if (!empty($statement)) {
try {
$this->updateData($statement);
} catch (DatabaseException $e) {
$errors[] = $e->getMessage();
}
}
}
if (!empty($errors)) {
$errortxt = "";
foreach ($errors as $error) {
$errortxt .= " {$error};";
}
$msg = "There were a number of issues: " . $errortxt;
throw new DatabaseException($msg);
}
} else {
$msg = "Elgg couldn't find the requested database script at " . $scriptlocation . ".";
throw new DatabaseException($msg);
}
}
/**
* Queue a query for execution upon shutdown.
*
* You can specify a handler function if you care about the result. This function will accept
* the raw result from {@link mysql_query()}.
*
* @param string $query The query to execute
* @param string $type The query type ('read' or 'write')
* @param string $handler A callback function to pass the results array to
*
* @return boolean Whether registering was successful.
* @todo deprecate passing resource for $type as that should not be part of public API
*/
public function registerDelayedQuery($query, $type, $handler = "") {
if (!is_resource($type) && $type != 'read' && $type != 'write') {
return false;
}
// Construct delayed query
$delayed_query = array();
$delayed_query['q'] = $query;
$delayed_query['l'] = $type;
$delayed_query['h'] = $handler;
$this->delayedQueries[] = $delayed_query;
return true;
}
/**
* Trigger all queries that were registered as "delayed" queries. This is
* called by the system automatically on shutdown.
*
* @return void
* @access private
* @todo make protected once this class is part of public API
*/
public function executeDelayedQueries() {
foreach ($this->delayedQueries as $query_details) {
try {
$link = $query_details['l'];
if ($link == 'read' || $link == 'write') {
$link = $this->getLink($link);
} elseif (!is_resource($link)) {
$msg = "Link for delayed query not valid resource or db_link type. Query: {$query_details['q']}";
$this->logger->log($msg, Elgg_Logger::WARNING);
}
$result = $this->executeQuery($query_details['q'], $link);
if ((isset($query_details['h'])) && (is_callable($query_details['h']))) {
$query_details['h']($result);
}
} catch (DatabaseException $e) {
// Suppress all exceptions since page already sent to requestor
$this->logger->log($e, Elgg_Logger::ERROR);
}
}
}
/**
* Enable the query cache
*
* This does not take precedence over the Elgg_Database_Config setting.
*
* @return void
*/
public function enableQueryCache() {
if ($this->config->isQueryCacheEnabled() && $this->queryCache === null) {
// @todo if we keep this cache, expose the size as a config parameter
$this->queryCache = new Elgg_Cache_LRUCache($this->queryCacheSize);
}
}
/**
* Disable the query cache
*
* This is useful for special scripts that pull large amounts of data back
* in single queries.
*
* @return void
*/
public function disableQueryCache() {
$this->queryCache = null;
}
/**
* Invalidate the query cache
*
* @return void
*/
protected function invalidateQueryCache() {
if ($this->queryCache) {
$this->queryCache->clear();
$this->logger->log("Query cache invalidated", Elgg_Logger::INFO);
}
}
/**
* Test that the Elgg database is installed
*
* @return void
* @throws InstallationException
*/
public function assertInstalled() {
if ($this->installed) {
return;
}
try {
$dblink = $this->getLink('read');
mysql_query("SELECT value FROM {$this->tablePrefix}datalists WHERE name = 'installed'", $dblink);
if (mysql_errno($dblink) > 0) {
throw new DatabaseException();
}
} catch (DatabaseException $e) {
throw new InstallationException("Unable to handle this request. This site is not configured or the database is down.");
}
$this->installed = true;
}
/**
* Get the number of queries made to the database
*
* @return int
*/
public function getQueryCount() {
return $this->queryCount;
}
/**
* Get the prefix for Elgg's tables
*
* @return string
*/
public function getTablePrefix() {
return $this->tablePrefix;
}
/**
* Sanitizes an integer value for use in a query
*
* @param int $value Value to sanitize
* @param bool $signed Whether negative values are allowed (default: true)
* @return int
*/
public function sanitizeInt($value, $signed = true) {
$value = (int) $value;
if ($signed === false) {
if ($value < 0) {
$value = 0;
}
}
return $value;
}
/**
* Sanitizes a string for use in a query
*
* @param string $value Value to escape
* @return string
*/
public function sanitizeString($value) {
return mysql_real_escape_string($value);
}
}
Fatal error: Uncaught exception 'DatabaseException' with message 'Table 'arteosak_elgg.elgg_users_sessions' doesn't exist
Something is not right with your database. At least the user_sessions table does not exist. So you have either not correctly restored the database from the backup or the database wasn't completely restored. Try again to restore the database from the backup.
I don't know what you want to show me with pasting the content of database.php though.
Right now this is not possible. In principle it should be possible to add such a functionality. But it isl not easy to achieve. The image uploads are organized in "batches" (all images you upload at the same time belong to a single batch which in turn also belongs to the album the images were uploaded to). There's also an album index created and maintained with all images of the album (this index is for example used when browsing through the images on an album). If you would now move an image from one album to another the container (= album) entry for this image would have to be updated - this would be the easiest, very trivial part. But in addition you would also have to update all the additional data structures (batches, album index etc.) accordingly. And this would require some work to implement it correctly (in addition to adjust the GUI to allow for moving the image).
The idea of allowing moving images between albums is surely a very nice one. I will keep it in mind and if I have time I might try to add this functionality to Tidypics. But I can't promise for this to happen soon.
info@elgg.org
Security issues should be reported to security@elgg.org!
©2014 the Elgg Foundation
Elgg is a registered trademark of Thematic Networks.
Cover image by Raül Utrera is used under Creative Commons license.
Icons by Flaticon and FontAwesome.