Changeset 17383


Ignore:
Timestamp:
Aug 4, 2012, 4:15:54 PM (12 years ago)
Author:
mistic100
Message:
  • simplify add page
  • simplify migration task
  • add option to add film effect on element edition page
Location:
extensions/gvideo
Files:
10 edited

Legend:

Unmodified
Added
Removed
  • extensions/gvideo/admin.php

    r17307 r17383  
    55
    66$conf['gvideo'] = unserialize($conf['gvideo']);
    7 load_language('plugin.lang', GVIDEO_PATH);
    87
    98// tabsheet
  • extensions/gvideo/admin/add.php

    r17310 r17383  
    1212    array_push($page['errors'], l10n('Please fill the video URL'));
    1313  }
    14   if ($_POST['name_server'] == 'false' and empty($_POST['name']))
    15   {
    16     array_push($page['errors'], l10n('Please fill the video name'));
    17   }
    18   if ($_POST['thumbnail_server'] == 'false' and empty($_FILES['thumbnail_src']))
    19   {
    20     array_push($page['errors'], l10n('Please add a thumbnail'));
    21   }
    2214  if ( !empty($_POST['url']) and ($video = parse_video_url($_POST['url'])) === false )
    2315  {
     
    2820  {
    2921    // download thumbnail
    30     $thumb_name = $video['type'].'-'.$video['id'].'-'.uniqid();
    31     if ($_POST['thumbnail_server'] == 'true')
     22    $thumb_name = $video['type'].'-'.$video['id'].'-'.uniqid().'.'.get_extension($video['thumbnail']);
     23    $thumb_source = $conf['data_location'].$thumb_name;
     24    if (download_remote_file($video['thumbnail'], $thumb_source) !== true)
    3225    {
    33       $thumb_name.= '.'.get_extension($video['thumbnail']);
    34       $thumb_source = $conf['data_location'].$thumb_name;
    35       if (download_remote_file($video['thumbnail'], $thumb_source) !== true)
    36       {
    37         $thumb_source = $conf['data_location'].get_filename_wo_extension($thumb_name).'.jpg';
    38         copy(GVIDEO_PATH.'mimetypes/'.$video['type'].'.jpg', $thumb_source);
    39       }
    40     }
    41     // upload thumbnail
    42     else
    43     {
    44       if ($_FILES['thumbnail_src']['error'] > 0)
    45       {
    46         array_push($page['errors'], l10n('Unknown upload error'));
    47       }
    48       else if ( !in_array($_FILES['thumbnail_src']['type'], array('image/jpeg','image/png','image/gif')) )
    49       {
    50         array_push($page['errors'], l10n('Incorrect file type,').' '.sprintf(l10n('Allowed file types: %s.'), 'jpg, png, gif'));
    51       }
    52      
    53       $thumb_name.= '.'.get_extension($_FILES['thumbnail_src']['name']);
    54       $thumb_source = $_FILES['thumbnail_src']['tmp_name'];
     26      $thumb_source = $conf['data_location'].get_filename_wo_extension($thumb_name).'.jpg';
     27      copy(GVIDEO_PATH.'mimetypes/'.$video['type'].'.jpg', $thumb_source);
    5528    }
    5629   
    57     if (count($page['errors']) == 0)
     30    if (isset($_POST['add_film_frame']))
    5831    {
    59       if (isset($_POST['add_film_frame']))
    60       {
    61         add_film_frame($thumb_source);
    62       }
     32      add_film_frame($thumb_source);
     33    }
     34   
     35    // add image and update infos
     36    $image_id = add_uploaded_file($thumb_source, $thumb_name, array($_POST['category']));
     37   
     38    $updates = array(
     39      'name' => pwg_db_real_escape_string($video['title']),
     40      'comment' => pwg_db_real_escape_string($video['description']),
     41      'author' => pwg_db_real_escape_string($video['author']),
     42      );
     43   
     44    single_update(
     45      IMAGES_TABLE,
     46      $updates,
     47      array('id' => $image_id),
     48      true
     49      );
     50   
     51    // register video
     52    if ($_POST['size_common'] == 'true')
     53    {
     54      $_POST['width'] = $_POST['height'] = '';
     55    }
     56    if ($_POST['autoplay_common'] == 'true')
     57    {
     58      $_POST['autoplay'] = '';
     59    }
     60   
     61    $insert = array(
     62      'picture_id' => $image_id,
     63      'url' => $video['url'],
     64      'type' => $video['type'],
     65      'video_id' => $video['id'],
     66      'width' => $_POST['width'],
     67      'height' => $_POST['height'],
     68      'autoplay' => $_POST['autoplay'],
     69      );
    6370     
    64       // add image and update infos
    65       $image_id = add_uploaded_file($thumb_source, $thumb_name, array($_POST['category']));
     71    single_insert(
     72      GVIDEO_TABLE,
     73      $insert
     74      );
    6675     
    67       if ($_POST['name_server'] == 'true')        $_POST['name'] = $video['title'];
    68       if ($_POST['description_server'] == 'true') $_POST['description'] = $video['description'];
    69       if ($_POST['author_server'] == 'true')      $_POST['author'] = $video['author'];
    70      
    71       $updates = array(
    72         'name' => pwg_db_real_escape_string($_POST['name']),
    73         'comment' => pwg_db_real_escape_string(@$_POST['description']),
    74         'author' => pwg_db_real_escape_string(@$_POST['author']),
    75         );
    76      
    77       single_update(
    78         IMAGES_TABLE,
    79         $updates,
    80         array('id' => $image_id),
    81         true
    82         );
    83      
    84       // register video
    85       if ($_POST['size_common'] == 'true')
    86       {
    87         $_POST['width'] = $_POST['height'] = '';
    88       }
    89       if ($_POST['autoplay_common'] == 'true')
    90       {
    91         $_POST['autoplay'] = '';
    92       }
    93      
    94       $insert = array(
    95         'picture_id' => $image_id,
    96         'type' => $video['type'],
    97         'video_id' => $video['id'],
    98         'width' => $_POST['width'],
    99         'height' => $_POST['height'],
    100         'autoplay' => $_POST['autoplay'],
    101         );
    102        
    103       single_insert(
    104         GVIDEO_TABLE,
    105         $insert
    106         );
    107        
    108       array_push($page['infos'], sprintf(
    109         l10n('Video successfully added. <a href="%s">View</a>'),
    110         make_picture_url(array(
    111           'image_id' => $image_id,
    112           'category' => array(
    113             'id' => $_POST['category'],
    114             'name' => '',
    115             'permalink' => '',
    116             ),
    117           ))
    118         ));
    119       unset($_POST);
    120     }
     76    array_push($page['infos'], sprintf(
     77      l10n('Video successfully added. <a href="%s">View</a>'),
     78      make_picture_url(array(
     79        'image_id' => $image_id,
     80        'category' => array(
     81          'id' => $_POST['category'],
     82          'name' => '',
     83          'permalink' => '',
     84          ),
     85        ))
     86      ));
     87    unset($_POST);
    12188  }
    12289}
     
    143110  'upload_max_filesize' => $upload_max_filesize,
    144111  'upload_max_filesize_shorthand' => $upload_max_filesize_shorthand,
     112  'gd_available' => function_exists('imagecreatetruecolor'),
    145113  'gvideo' => $conf['gvideo'],
    146114  'POST' => @$_POST,
  • extensions/gvideo/admin/template/add.tpl

    r17310 r17383  
    3737      </label>
    3838    </li>
    39     <li>
    40       <span class="property">{'Name'|@translate}</span>
    41       <label><input type="radio" name="name_server" value="true" {if $POST.name_server != 'false'}checked="checked"{/if} data-toggle="name"> {'From the video'|@translate}</label>
    42       <label><input type="radio" name="name_server" value="false" {if $POST.name_server == 'false'}checked="checked"{/if} data-toggle="name"> {'Change'|@translate}</label>
    43     </li>
    44     <li {if $POST.name_server != 'false'}style="display:none;"{/if} id="name">
    45       <span class="property">&nbsp;</span>
    46       <input type="text" name="name" value="{$POST.name}" size="70">
    47     </li>
    48     <li>
    49       <span class="property">{'Author'|@translate}</span>
    50       <label><input type="radio" name="author_server" value="true" {if $POST.author_server != 'false'}checked="checked"{/if} data-toggle="author"> {'From the video'|@translate}</label>
    51       <label><input type="radio" name="author_server" value="false" {if $POST.author_server == 'false'}checked="checked"{/if} data-toggle="author"> {'Change'|@translate}</label>
    52     </li>
    53     <li {if $POST.author_server != 'false'}style="display:none;"{/if} id="author">
    54       <span class="property">&nbsp;</span>
    55       <input type="text" name="author" value="{$POST.author}" size="20">
    56     </li>
    57     <li>
    58       <span class="property">{'Description'|@translate}</span>
    59       <label><input type="radio" name="description_server" value="true" {if $POST.description_server != 'false'}checked="checked"{/if} data-toggle="description"> {'From the video'|@translate}</label>
    60       <label><input type="radio" name="description_server" value="false" {if $POST.description_server == 'false'}checked="checked"{/if} data-toggle="description"> {'Change'|@translate}</label>
    61     </li>
    62     <li {if $POST.description_server != 'false'}style="display:none;"{/if} id="description">
    63       <span class="property">&nbsp;</span>
    64       <textarea name="description" rows="5" cols="50">{$POST.description}</textarea>
    65     </li>
     39  {if $gd_available}
    6640    <li>
    6741      <span class="property">{'Thumbnail'|@translate}</span>
    68       <label><input type="radio" name="thumbnail_server" value="true" checked="checked" data-toggle="thumbnail_src"> {'From the video'|@translate}</label>
    69       <label><input type="radio" name="thumbnail_server" value="false" data-toggle="thumbnail_src"> {'Change'|@translate}</label>
    70     </li>
    71     <li id="thumbnail_src" style="display:none;">
    72       <span class="property">&nbsp;</span>
    73       <input type="file" name="thumbnail_src"><br>
    74       <span class="property">&nbsp;</span>
    75       {'Maximum file size: %sB.'|@translate|@sprintf:$upload_max_filesize_shorthand} {'Allowed file types: %s.'|@translate|@sprintf:'jpg, png, gif'}
    76       <input type="hidden" name="MAX_FILE_SIZE" value="{$upload_max_filesize}">
    77     </li>
    78     <li>
    79       <span class="property">&nbsp;</span>
    8042      <label><input type="checkbox" name="add_film_frame" value="true"> {'Add film effect'|@translate} </label>
    8143      <a class="showInfo" title="<img src='{$GVIDEO_PATH}admin/template/example-frame.jpg'>">i</a>
    8244    </li>
     45  {/if}
    8346  </ul> 
    8447</fieldset>
     
    11780  <legend>{'Supported services'|@translate}</legend>
    11881 
    119   <ul>
     82  <ul class="services">
    12083    <li>
    12184      <img class="icon" src="{$GVIDEO_PATH}admin/template/icons/youtube.png">
  • extensions/gvideo/admin/template/style.css

    r17310 r17383  
    5252  font-size:0.8em;
    5353}
     54
     55ul.services li {
     56  display:inline;
     57  margin-left:20px;
     58}
  • extensions/gvideo/include/functions.inc.php

    r17311 r17383  
    4040      $json = json_decode($json, true);
    4141      $video = array_merge($video, array(
     42        'url' => 'http://youtube.com/watch?v='.$video['id'],
    4243        'title' => $json['entry']['title']['$t'],
    4344        'description' => $json['entry']['media$group']['media$description']['$t'],
     
    6263      $json = json_decode($json, true);
    6364      $video = array_merge($video, array(
     65        'url' => 'http://vimeo.com/'.$video['id'],
    6466        'title' => $json[0]['title'],
    6567        'description' => $json[0]['description'],
     
    8890      $video = array_merge($video, array(
    8991        'id' => $json['id'],
     92        'url' => 'http://dailymotion.com/video/'.$json['id'],
    9093        'title' => $json['title'],
    9194        'description' => $json['description'],
     
    108111      $video['id'] = $matches[1];
    109112     
     113      $video['url'] = $source_url;
     114     
    110115      preg_match('#<meta name="name" content="([^">]*)" />#', $html, $matches);
    111116      $video['title'] = $matches[1];
     
    131136      $html = download_remote_file($source_url, true);
    132137      if ($html === false or $html == 'file_error') return false;
    133            
     138     
     139      $video['url'] = 'http://wideo.fr/video/'.$video['id'].'.html';
     140     
    134141      preg_match('#<meta property="og:title" content="([^">]*)" />#', $html, $matches);
    135142      $video['title'] = $matches[1];
     
    166173      $html = download_remote_file($source_url, true);
    167174      if ($html === false or $html == 'file_error') return false;
    168            
     175     
     176      $video['url'] = 'http://www.videobb.com/video/'.$video['id'];
     177     
    169178      preg_match('#<meta content="videobb - ([^">]*)"  name="title" property="" />#', $html, $matches);
    170179      $video['title'] = $matches[1];
  • extensions/gvideo/include/gvideo.inc.php

    r17307 r17383  
    7070
    7171/**
    72  * add message on edition page
     72 * add message and link on edition page
    7373 */
    7474function gvideo_photo_edit()
    7575{
    76   global $page;
     76  global $page, $template, $conf;
    7777 
    7878  if ($page['page'] != 'photo') return;
     
    8585  $result = pwg_query($query);
    8686 
    87   if (pwg_db_num_rows($result))
     87  if (!pwg_db_num_rows($result)) return;
     88   
     89  $video = pwg_db_fetch_assoc($result);
     90   
     91  array_push($page['warnings'], l10n('This element is a video added with "Embedded Video"'));
     92 
     93  if (function_exists('imagecreatetruecolor'))
    8894  {
    89     array_push($page['warnings'], l10n('This element is a video added with "Embedded Video"'));
     95    if (isset($_GET['add_film_frame']))
     96    {
     97      include_once(GVIDEO_PATH . '/include/functions.inc.php');
     98      include_once(PHPWG_ROOT_PATH . 'admin/include/functions_upload.inc.php');
     99     
     100      $image_infos = pwg_db_fetch_assoc(pwg_query('SELECT path, file FROM '.IMAGES_TABLE.' WHERE id = '.$_GET['image_id'].';'));
     101      $thumb_source = $conf['data_location'].$image_infos['file'];
     102     
     103      add_film_frame($image_infos['path'], $thumb_source);
     104      add_uploaded_file($thumb_source, $image_infos['file'], null, null, $_GET['image_id']);
     105      $template->assign('TN_UPDATE', true);
     106    }
     107   
     108    $admin_photo_base_url = get_root_url().'admin.php?page=photo-'.$_GET['image_id'];
     109    $admin_url_start = $admin_photo_base_url.'-properties';
     110    $admin_url_start.= isset($_GET['cat_id']) ? '&amp;cat_id='.$_GET['cat_id'] : '';
     111   
     112    $template->assign('U_ADD_FILM_FRAME', $admin_url_start.'&amp;add_film_frame=1');
     113    $template->set_prefilter('picture_modify', 'gvideo_photo_edit_prefilter');
    90114  }
    91115}
    92116
     117function gvideo_photo_edit_prefilter($content)
     118{
     119  $search[0] = '{if !url_is_remote($PATH)}';
     120  $replace[0] = '<li><a href="{$U_ADD_FILM_FRAME}" id="refresh_video">{\'Add film effect\'|@translate}</a></li>'.$search[0];
     121 
     122  $search[1] = '{$TN_SRC}';
     123  $replace[1] = $search[1].'{if $TN_UPDATE}?'.time().'{/if}';
     124 
     125  return str_replace($search, $replace, $content);
     126}
     127
    93128?>
  • extensions/gvideo/language/en_UK/plugin.lang.php

    r17310 r17383  
    33$lang['Add a video'] = 'Add a video';
    44$lang['Video URL'] = 'Video URL';
    5 $lang['From the video'] = 'From the video';
    6 $lang['Change'] = 'Change';
    75$lang['Video size'] = 'Video size';
    86$lang['Use common setting'] = 'Use common setting';
     
    1513$lang['Logo'] = 'Logo';
    1614$lang['Please fill the video URL'] = 'Please fill the video URL';
    17 $lang['Please fill the video name'] = 'Please fill the video name';
    18 $lang['Please add a thumbnail'] = 'Please add a thumbnail';
    1915$lang['Unable to contact host server'] = 'Unable to contact host server';
    2016$lang['Video successfully added. <a href="%s">View</a>'] = 'Video successfully added. <a href="%s">View</a>';
  • extensions/gvideo/language/fr_FR/plugin.lang.php

    r17310 r17383  
    33$lang['Add a video'] = 'Ajouter une vidéo';
    44$lang['Video URL'] = 'URL de la vidéo';
    5 $lang['From the video'] = 'Conserver';
    6 $lang['Change'] = 'Changer';
    75$lang['Video size'] = 'Taille de la vidéo';
    86$lang['Use common setting'] = 'Utiliser les paramètres généraux';
     
    1513$lang['Logo'] = 'Logo';
    1614$lang['Please fill the video URL'] = 'Veuillez renseigner l\'URL de la vidéo';
    17 $lang['Please fill the video name'] = 'Veuillez renseigner le nom de la vidéo';
    18 $lang['Please add a thumbnail'] = 'Veuillez choisir une miniature';
    1915$lang['Unable to contact host server'] = 'Impossible de contacter le serveur';
    2016$lang['Video successfully added. <a href="%s">View</a>'] = 'Vidéo ajoutée. <a href="%s">Voir</a>';
  • extensions/gvideo/main.inc.php

    r17307 r17383  
    2626  add_event_handler('loc_begin_admin_page', 'gvideo_photo_edit');
    2727  add_event_handler('get_admin_plugin_menu_links', 'gvideo_admin_menu');
     28  add_event_handler('init', 'gvideo_init');
     29 
     30  function gvideo_init()
     31  {
     32    load_language('plugin.lang', GVIDEO_PATH);
     33  }
    2834
    2935  function gvideo_admin_menu($menu)
  • extensions/gvideo/maintain.inc.php

    r17310 r17383  
    3131  );
    3232
    33 
     33/* install */
    3434function plugin_install()
    3535{
     
    4141CREATE TABLE IF NOT EXISTS `'.gvideo_table.'` (
    4242  `picture_id` mediumint(8) NOT NULL,
     43  `url` varchar(255) DEFAULT NULL,
    4344  `type` varchar(64) NOT NULL,
    4445  `video_id` varchar(64) NOT NULL,
    4546  `width` smallint(9) DEFAULT NULL,
    4647  `height` smallint(9) DEFAULT NULL,
    47   `autoplay` tinyint(1) DEFAULT NULL
     48  `autoplay` tinyint(1) DEFAULT NULL,
    4849) ENGINE=MyISAM DEFAULT CHARSET=utf8
    4950;';
     
    5960}
    6061
     62/* activate */
    6163function plugin_activate()
    6264{
     
    6769    plugin_install();
    6870  }
    69   else if (!isset($conf['gvideo']))
    70   {
    71     conf_update_param('gvideo', gvideo_default_config);
    72   }
    73 }
    74 
     71  else
     72  {
     73    if (!isset($conf['gvideo']))
     74    {
     75      conf_update_param('gvideo', gvideo_default_config);
     76    }
     77   
     78    $result = pwg_query('SHOW COLUMNS FROM '.gvideo_table.' LIKE "url";');
     79    if (!pwg_db_num_rows($result))
     80    {     
     81      pwg_query('ALTER TABLE '.gvideo_table.' ADD `url` VARCHAR(255) DEFAULT NULL;');
     82    }
     83  }
     84}
     85
     86/* uninstall */
    7587function plugin_uninstall()
    7688
     
    106118  }
    107119 
     120  if (!isset($conf['prefix_thumbnail']))
     121  {
     122    $conf['prefix_thumbnail'] = 'TN-';
     123  }
     124
     125  if (!isset($conf['dir_thumbnail']))
     126  {
     127    $conf['dir_thumbnail'] = 'thumbnail';
     128  }
     129 
    108130  set_time_limit(600);
    109131  include_once(gvideo_path . '/include/functions.inc.php');
     
    123145    {
    124146      case 'vimeo':
    125         $url = 'http://vimeo.com/'.$file['id'];
     147        $video = array(
     148          'type' => 'vimeo',
     149          'url' => 'http://vimeo.com/'.$file['id'],
     150          );
    126151        break;
    127152      case 'dm':
    128         $url = 'http://dailymotion.com/video/'.$file['id'];
     153        $video = array(
     154          'type' => 'dailymotion',
     155          'url' => 'http://dailymotion.com/video/'.$file['id'],
     156          );
    129157        break;
    130158      case 'ytube':
    131         $url = 'http://youtu.be/'.$file['id'];
     159        $video = array(
     160          'type' => 'youtube',
     161          'url' => 'http://youtube.com/watch?v='.$file['id'],
     162          );
    132163        break;
    133164      case 'wideo':
    134         $url = 'http://wideo.fr/video/'.$file['id'].'.html';
    135         break;
    136        
    137       case 'wat': // can't get original page from id !
    138         $thumb = str_replace($img['file'], null, $img['path']).'thumbnail/TN-'.get_filename_wo_extension($img['file']).'.*';
    139         $thumb = glob($thumb);
    140         if (!empty($thumb))
    141         {
    142           $thumb_name = 'wat-'.$file['id'].'-'.uniqid().'.'.get_extension($thumb[0]);
    143           $thumb_source = $conf['data_location'].$thumb_name;
    144           copy($thumb[0], $thumb_source);
    145         }
    146        
     165        $video = array(
     166          'type' => 'wideo',
     167          'url' => 'http://wideo.fr/video/'.$file['id'].'.html',
     168          );
     169        break;
     170      case 'wat':
    147171        $video = array(
    148172          'type' => 'wat',
    149           'id' => $file['id'],
    150           'title' => null,
    151           'description' => null,
    152           'thumbnail' => null,
    153           );
    154         break;
    155        
     173          'url' => null,
     174          );
     175        break;
    156176      case 'gvideo': // closed
    157177      default:
     
    160180    }
    161181   
    162     // get video infos
    163     if (!isset($video))
    164     {
    165       if ( ($video = parse_video_url($url)) === false )
    166       {
    167         array_push($images_delete, $img['id']);
    168         continue;
    169       }
    170     }
    171    
    172     // download thumbnail
    173     if (!isset($thumb_source))
    174     {
    175       $thumb_name = $video['type'].'-'.$video['id'].'-'.uniqid().'.'.get_extension($video['thumbnail']);
     182    $real_path = str_replace($img['file'], null, str_replace('././', './', $img['path']));
     183   
     184    // get existing thumbnail
     185    $thumb = $real_path.$conf['dir_thumbnail'].'/'.$conf['prefix_thumbnail'].get_filename_wo_extension($img['file']).'.*';
     186    $thumb = glob($thumb);
     187    if (!empty($thumb))
     188    {
     189      $thumb_name = $video['type'].'-'.$file['id'].'-'.uniqid().'.'.get_extension($thumb[0]);
    176190      $thumb_source = $conf['data_location'].$thumb_name;
    177       if (download_remote_file($video['thumbnail'], $thumb_source) !== true)
    178       {
    179         $thumb_source = $conf['data_location'].get_filename_wo_extension($thumb_name).'.jpg';
    180         copy(gvideo_path.'mimetypes/'.$video['type'].'.jpg', $thumb_source);
    181         add_film_frame($thumb_source);
    182       }
     191      copy($thumb[0], $thumb_source);
     192    }
     193    else
     194    {
     195      $thumb_name = $video['type'].'-'.$file['id'].'-'.uniqid().'.jpg';
     196      $thumb_source = $conf['data_location'].$thumb_name;
     197      copy(gvideo_path.'mimetypes/'.$video['type'].'.jpg', $thumb_source);
     198      add_film_frame($thumb_source);
    183199    }
    184200   
     
    187203   
    188204    // update path and rename the file
    189     $img['new_path'] = str_replace($img['file'], null, $img['path']).$thumb_name;
     205    $img['new_path'] = $real_path.$thumb_name;
    190206    rename($img['path'], $img['new_path']);
    191207    array_push($images_updates, array(
    192208      'id' => $img['id'],
    193       'path' => str_replace('././', './', $img['new_path']),
     209      'path' => $img['new_path'],
    194210      ));
    195211   
     
    200216    array_push($videos_inserts, array(
    201217      'picture_id' => $image_id,
     218      'url' => $video['url'],
    202219      'type' => $video['type'],
    203       'video_id' => $video['id'],
     220      'video_id' => $file['id'],
    204221      'width' => $file['width'],
    205222      'height' => $file['height'],
     
    216233  mass_inserts(
    217234    gvideo_table,
    218     array('picture_id', 'type', 'video_id', 'width', 'height', 'autoplay'),
     235    array('picture_id', 'url', 'type', 'video_id', 'width', 'height', 'autoplay'),
    219236    $videos_inserts
    220237    );
Note: See TracChangeset for help on using the changeset viewer.