/**
  * Process bulk add/remove/etc operations
  *
  * @param Bin    $rec
  * @param string $bulk_op
  * @param array  $data
  * @param string $notes
  */
 protected function run_bulk($rec, $bulk_op, $data, $notes = null)
 {
     // check for write-authz on bin (for everything EXCEPT random)
     if ($bulk_op != 'bulk_random' && !$rec->user_may_write($this->user)) {
         $msg = "Insufficient authz for {$bulk_op}";
         throw new Rframe_Exception(Rframe::BAD_AUTHZ, $msg);
     }
     $this->bulk_op = $bulk_op;
     // rethrow any exceptions as 'data' exceptions
     try {
         if ($bulk_op == 'bulk_add') {
             $this->bulk_rs = AIR2Bin::add_sources($rec, $data, $notes);
         }
         if ($bulk_op == 'bulk_addsub') {
             $this->bulk_rs = AIR2Bin::add_submissions($rec, $data, $notes);
         }
         if ($bulk_op == 'bulk_addsearch') {
             $this->bulk_rs = AIR2Bin::add_search($this->user, $rec, $data, $notes);
         }
         if ($bulk_op == 'bulk_addtank') {
             $tanks = array();
             $data = is_array($data) ? array_unique($data) : array($data);
             foreach ($data as $uuid) {
                 $t = AIR2_Record::find('Tank', $uuid);
                 if (!$t) {
                     throw new Exception("Invalid tank_uuid({$uuid})");
                 }
                 $tanks[] = $t;
             }
             foreach ($tanks as $t) {
                 $counts = AIR2Bin::add_tank($rec, $t, $notes);
                 foreach ($counts as $key => $val) {
                     if (!isset($this->bulk_rs[$key])) {
                         $this->bulk_rs[$key] = 0;
                     }
                     $this->bulk_rs[$key] += $counts[$key];
                 }
             }
         }
         if ($bulk_op == 'bulk_addbin') {
             $bins = array();
             $data = is_array($data) ? array_unique($data) : array($data);
             foreach ($data as $uuid) {
                 $b = AIR2_Record::find('Bin', $uuid);
                 if (!$b) {
                     throw new Exception("Invalid bin_uuid({$uuid})");
                 }
                 if (!$b->user_may_read($this->user)) {
                     throw new Exception("Invalid bin_uuid({$uuid})");
                 }
                 $bins[] = $b;
             }
             foreach ($bins as $b) {
                 $counts = AIR2Bin::add_bin($rec, $b, $notes);
                 foreach ($counts as $key => $val) {
                     if (!isset($this->bulk_rs[$key])) {
                         $this->bulk_rs[$key] = 0;
                     }
                     $this->bulk_rs[$key] += $counts[$key];
                 }
             }
         }
         if ($bulk_op == 'bulk_remove') {
             $this->bulk_rs = AIR2Bin::remove_sources($rec, $data);
         }
         if ($bulk_op == 'bulk_removeall') {
             $this->bulk_rs = AIR2Bin::remove_all_sources($rec, $data);
         }
         if ($bulk_op == 'bulk_random') {
             $this->bulk_rs = AIR2Bin::randomize($this->user, $rec, $data);
         }
         if ($bulk_op == 'bulk_tag') {
             $this->bulk_rs = AIR2Bin::tag_sources($this->user, $rec, $data);
         }
         if ($bulk_op == 'bulk_annot') {
             $this->bulk_rs = AIR2Bin::annotate_sources($this->user, $rec, $data);
         }
     } catch (Rframe_Exception $e) {
         throw $e;
         //re-throw as-is
     } catch (Exception $e) {
         throw new Rframe_Exception(Rframe::BAD_DATA, $e->getMessage());
     }
 }
 /**
  * Create new bins, with random content from an original bin
  *
  * @param  User   $u
  * @param  Bin    $bin
  * @param  array  $params
  * @return array  $counts
  */
 public static function randomize($u, $bin, $params)
 {
     $stats = self::init_stats($stats = array('total', 'insert', 'duplicate', 'invalid'));
     $stats['bin_uuids'] = array();
     $num = isset($params['num']) ? $params['num'] : 1;
     $size = isset($params['size']) ? $params['size'] : null;
     // sanity checking
     $conn = AIR2_DBManager::get_master_connection();
     $q = "select count(*) from bin_source where bsrc_bin_id = ?";
     $total = $conn->fetchOne($q, array($bin->bin_id), 0);
     if ($num < 0 || $num > 20 || $num > $total) {
         throw new Exception("Invalid num {$num} for randomize!");
     }
     if ($size && ($size < 1 || $size * $num > $total)) {
         throw new Exception("Invalid size {$size} for randomize (total={$total} num={$num})!");
     }
     // calculate size of each bin
     $bin_sizes = array_fill(0, $num, $size ? $size : 0);
     if (!$size) {
         $sz_idx = 0;
         for ($i = 0; $i < $total; $i++) {
             $bin_sizes[$sz_idx]++;
             $sz_idx = ($sz_idx + 1) % $num;
         }
     }
     // select in random order (mysql does this faster now than previous versions)
     $q = "select bsrc_src_id from bin_source where bsrc_bin_id = ? order by RAND()";
     if ($size) {
         $q .= " limit " . $num * $size;
     }
     $src_ids = $conn->fetchColumn($q, array($bin->bin_id), 0);
     // create bins, and insert items
     $curr_offset = 0;
     foreach ($bin_sizes as $bidx => $bsize) {
         $b = new Bin();
         $b->bin_user_id = $u->user_id;
         $b->bin_name = "{$bin->bin_name} - Random " . ($bidx + 1);
         $b->bin_desc = "Random output from bin: '{$bin->bin_name}'";
         $b->save();
         $stats['bin_uuids'][] = $b->bin_uuid;
         $add_ids = array_slice($src_ids, $curr_offset, $bsize);
         $curr_offset += $bsize;
         $bstats = AIR2Bin::add_sources($b, $add_ids);
         $stats['insert'] += $bstats['insert'];
         $stats['total'] += $bstats['total'];
     }
     return $stats;
 }