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:     replace_file(File, RscId, [], Context).
  
  replace_file(File, RscId, Props, Context) ->
      case z_acl:rsc_editable(RscId, Context) orelse not(m_rsc:p(RscId, is_authoritative, Context)) of
          true ->
              OriginalFilename = proplists:get_value(original_filename, Props, File),
              PropsMedia = add_medium_info(File, OriginalFilename, [{original_filename, OriginalFilename}], 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 ->
                      SafeRootName = z_string:to_rootname(OriginalFilename),
                      SafeFilename = SafeRootName ++ z_media_identify:extension(proplists:get_value(mime, PropsMedia)),
                      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 ->
                              case Mime of
                                  "image/" ++ _ -> m_rsc:update(RscId, [{category, image}], Context);
                                  "video/" ++ _ -> m_rsc:update(RscId, [{category, video}], Context);
                                  "audio/" ++ _ -> m_rsc:update(RscId, [{category, audio}], Context);
                                  _ -> nop
                              end;
                          false -> nop
                      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, file_not_allowed}
              end;
          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 ->
              Url1 = z_convert:to_list(Url),
              Props1 = [{original_filename, filename:basename(Url1)} | Props],
              File = z_utils:tempfile(),
              case http:request(get, 
                                {Url1, []},
                                [],
                                [{stream, File}]) of
                  {ok, saved_to_file} ->
                      {ok, RscId} = replace_file(File, RscId, Props1, Context),
                      ok = file:delete(File),
                      {ok, RscId};
                  {error, E} ->
                      {error, E}
              end;
  
negative.code: %% @doc Make a new resource for the file, when the file is not in the archive dir then a copy is made in the archive dir
  %% @spec insert_file(File, Context) -> {ok, Id} | {error, Reason}
  insert_file(File, Context) ->
      insert_file(File, [], Context).
  
  insert_file(File, Props, Context) ->
      OriginalFilename = proplists:get_value(original_filename, Props, File),
      PropsMedia = add_medium_info(File, OriginalFilename, [{original_filename, OriginalFilename}], Context),
      insert_file(File, Props, PropsMedia, Context).
  
  insert_file(File, 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 ->
              insert_file_mime_ok(File, Props, PropsMedia, Context);
          false ->
              {error, file_not_allowed}
      end.
  
  %% @doc Make a new resource for the file based on a URL.
  %% @spec insert_file(File, Context) -> {ok, Id} | {error, Reason}
  insert_url(Url, Context) ->
      insert_url(Url, [], Context).
  insert_url(Url, Props, Context) ->
      case download_file(Url) of
          {ok, File} ->
              Result = insert_file(File, [{original_filename, filename:basename(Url)}|Props], Context),
              file:delete(File),
              Result;
          {error, Reason} ->
              {error, Reason}
      end.
  
  
  %% Perform the resource management around inserting a file. The ACL is already checked for the mime type.
  %% Runs the final insert inside a transaction so that we can rollback.
  insert_file_mime_ok(File, Props, PropsMedia, Context) ->
      Props1 = case proplists:get_value(category_id, Props, proplists:get_value(category, Props)) of
          undefined -> [{category, mime_to_category(proplists:get_value(mime, PropsMedia))} | Props];
          _ -> Props
      end,
      Props2 = case proplists:get_value(is_published, Props1) of
          undefined -> [{is_published, true} | Props1];
          _ -> Props1
      end,
      InsertFun = fun(Ctx) ->
          case m_rsc:insert(Props2, Ctx) of
              {ok, Id} ->
                  replace_file_mime_ok(File, Id, Props2, PropsMedia, Ctx);
              Error ->
                  Error
          end
  