anchor.diff:      insert_file/3,
       replace_file/3,
       replace_file/4,
  +    replace_file/5,
       insert_url/2,
       insert_url/3,
       replace_url/4,
  
positive.code:     end.
  
  
      rsc_is_media_cat(Id, Context) ->
          case z_db:q1("select c.name from rsc c join rsc r on r.category_id = c.id where r.id = $1", [Id], Context) of
              <<"media">> -> true;
              <<"image">> -> true;
              <<"audio">> -> true;
              <<"video">> -> true;
              _ -> false
          end.
  
  
  
  %% @doc Fetch the medium information of the file, if they are not set in the Props
  
negative.code:     replace_file(File, RscId, [], Context).
  
  replace_file(File, RscId, Props, Context) ->
      OriginalFilename = proplists:get_value(original_filename, Props, File),
      PropsMedia = add_medium_info(File, OriginalFilename, [{original_filename, OriginalFilename}], Context),
      replace_file(File, RscId, Props, PropsMedia, Context).
  
  replace_file(File, RscId, Props, PropsMedia, Context) ->
      Mime = proplists:get_value(mime, PropsMedia),
      case z_acl:is_allowed(insert, #acl_media{mime=Mime, size=filelib:file_size(File)}, Context) of
          true ->
              replace_file_mime_ok(File, RscId, Props, PropsMedia, Context);
          false ->
              {error, file_not_allowed}
      end.
  
      %% @doc Replace the file, no mime check needed.
      replace_file_mime_ok(File, RscId, Props, PropsMedia, Context) ->
          ?DEBUG({replace_file_mime_ok, File, RscId, Props, PropsMedia}),
          case z_acl:rsc_editable(RscId, Context) orelse not(m_rsc:p(RscId, is_authoritative, Context)) of
              true ->
                  Mime = proplists:get_value(mime, PropsMedia),
                  SafeRootName = z_string:to_rootname(proplists:get_value(original_filename, Props, File)),
                  SafeFilename = SafeRootName ++ z_media_identify:extension(Mime),
                  ArchiveFile = z_media_archive:archive_copy_opt(File, SafeFilename, Context),
                  RootName = filename:rootname(filename:basename(ArchiveFile)),
                  MediumRowProps = [
                      {id, RscId}, 
                      {filename, ArchiveFile}, 
                      {rootname, RootName}, 
                      {is_deletable_file, not z_media_archive:is_archived(File, Context)}
                      | PropsMedia
                  ],
  
                  %% When the resource is in the media category, then move it to the correct sub-category depending
                  %% on the mime type of the uploaded file.
                  case rsc_is_media_cat(RscId, Context) of
                      true -> m_rsc:update(RscId, [{category, mime_to_category(Mime)}], Context);
                      false -> m_rsc:touch(RscId, Context)
                  end,
  
                  F = fun(Ctx) ->
                      z_db:delete(medium, RscId, Context),
                      case z_db:insert(medium, MediumRowProps, Ctx) of
                          {ok, _MediaId} ->
                              {ok, RscId};
                          Error ->
                              Error
                      end
                  end,
  
                  Depicts = depicts(RscId, Context),
                  {ok, Id} = z_db:transaction(F, Context),
                  [ z_depcache:flush(DepictId, Context) || DepictId <- Depicts ],
                  z_depcache:flush(Id, Context),
                  m_rsc:get(Id, Context), %% Prevent side effect that empty things are cached?
                  %% Pass the medium record along in the notification; this also fills the depcache (side effect).
                  z_notifier:notify({media_replace_file, Id, m_media:get(Id, Context)}, Context),
                  {ok, Id};
              false ->
                  {error, eacces}
          end.
  
  
  replace_url(Url, RscId, Props, Context) ->
      case z_acl:rsc_editable(RscId, Context) orelse not(m_rsc:p(RscId, is_authoritative, Context)) of
          true ->
              case download_file(Url) of
                  {ok, File} ->
                      Result = replace_file(File, RscId, [{original_filename, filename:basename(Url)}|Props], Context),
                      file:delete(File),
                      Result;
                  {error, E} ->
                      {error, E}
              end;
  